面向对象(Java script)
面向对象的概念:OOP(面向对象):
全称Object Oriented Programming
不是一种语法,是一种编程思想;js中的面向对象编程,就是创建对象,给对象添加属性和方法。
创建对象:
直接创建:
var obj = { };
这种创建也叫字面量的方式。就是你一眼看到就能知道键的值是多少。
例如:
var obj = {
name:"张三",
age:12,
sex:"男"
}
构造函数方式创建:
var obj = new object();
object是一个系统提供的改造函数,这个构造函数专门用来创建对象的。
给这种对象添加属性和方法的语法:
对象.属性名 = 值;
对象.方法名 = 函数;
例如:
从上面可以看出,每次创建对象都是重复的动作,我们可以用函数来处理。
概念:定义一个函数,每次调用都能得到一个对象,这种调用就能创建对象的函数,就是工厂函数;
示例:
function createObj(name,age,sex){
var obj = new Object();
obj.name = name;
obj.age = age;
obj.sex = sex;
return obj;
}
var obj1 = createObj("张三",12,"男");
console.log(obj1)
var obj2 = createObj("李四",13,"女");
console.log(obj2)
结果:
这种调用就能创建对象的函数,叫做工厂函数。创建出来的每个对象的结构一致。
优点:可以同时创建多个对象;
缺点:创建出来的没有具体的类型(比如是Array和Number),都是object类型的,但我们看到自己的对象只是object,不知道具体是什么类型。
解决方案:自定义构造函数。其实就是自己写一个函数,专门用来new对象。
对于Number、String构造函数来说,他们都是系统内置的函数。
我们自己创建出来的对象之所以都是Object,是因为我们创建对象的时候使用的是new object(); Object也是系统内置的一个构造函数。
例如:
所以我们也要自定义一个构造函数,创建出来的对象也就能看出具体是什么类型的对象。
示例:
function Person(){
name = "李四";
age = 12;
sex = "男";
}
var arr = new Person();
function Dog(){
name = "小白";
sex = "公";
}
var brr = new Dog();
console.log(arr);
console.log(brr);
输出结果:
自定义构造函数:
function Person(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
var obj1 = new Person("张三",12,"男");
console.log(obj1)
var obj2 = new Person("李四",13,"女");
console.log(obj2)
构造函数和普通函数不同的地方在于使用new的时候,中间发生了很多看不见的过程:
- 创建了一个新对象(这个过程是隐形的)
- this指向了这个新对象(新对象就有了属性,创建了属性)
- 执行构造函数,也就是调用了这个函数(给对象添加属性和方法,给属性和方法赋值)
- 返回这个新对象(返回结果也是隐形的)
使用new构造函数来创建对象的过程称之为实例化。
构造函数注意事项:
- 构造函数天生就是用来创建对象的,所以必须和new配合使用,否则就不具备创建对象的能力;
- 构造函数内部不能有return关键字,因为构造函数会自动返回对象。如果返回基本数据类型,和不加效果一样,如果返回复杂数据类型,构造函数就没意义了。
- 如果new的时候,不需要参数,那么小括号可以省略;
- 人们通常将构造函数的首字母大写。
一个构造函数中给对象添加了一个方法,然后创建了两个对象,这两个对象都有这个方法,并且这两个方法一模一样,但是这个方法在内存中却是两个空间,这样对于内存空间来说有点浪费,因为两个对象的方法是一样的但是却占了两个空间。
如下示例:
function Person(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
this.say = function(){
console.log('讲话')
}
}
var obj1 = new Person("张三",12,"男");
console.log(obj1)
var obj2 = new Person("李四",13,"女");
console.log(obj2)
console.log(obj1.say === obj2.say);
两个结果相等比较是false表示这是两个空间。
存储对象方法的具体过程如下图:
如果将方法定义在对象外面,可以解决这个问题:
function Person(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
this.say = fn;
}
function fn(){
console.log('讲话')
}
var obj1 = new Person("张三",12,"男");
console.log(obj1)
var obj2 = new Person("李四",13,"女");
console.log(obj2)
console.log(obj1.say === obj2.say);
</script>
</html>
这样比较结果就是true。
但是在后期我们使用插件或者定义别的函数的时候有可能会造成覆盖的现象。所以这个解决方案解决不彻底。
使用原型可以完美解决这个问题。
原型
概念:任何函数在创建好的时候,浏览器会分配一个对象给这个函数,通过函数的prototype可以访问到这个对象。这个对象叫做原型对象,简称原型。通过new构造函数实例化出来的对象默认可以访问到这个对象的属性和方法。
任何对象天生自带一个属性,叫 __poroto__ 值是一个对象,这个对象叫做原型/原型对象。
例如:
function fn(){}
console.log(fn.prototype);
给这个原型添加属性和方法,使用实例对象可以访问到:
function Person(){ } Person.prototype.name = '赵六'; Person.prototype.say=function(){ console.log("说话"); } var obj = new Person(); console.log(obj); obj.say();
对象访问属性的时候,先找自己有没有,自己有就直接使用,自己没有就去原型对象上找。
再例如:
function Person(){
}
Person.prototype.name = '赵六';
Person.prototype.say=function(){
console.log("说话");
}
var obj1 = new Person();
var obj2 = new Person();
console.log(obj1.say == obj2.say);//ture
true 表示这是同一个内存空间,因为两个对象访问的这个方法是Person这个函数对应的原型对象上的方法,两个对象用的是同一个构造函数Person,所以是相等。这样就能解决上面构造函数的问题了。
我们以后创建对象就使用构造函数,多个对象要使用同一个方法或属性,就把这个属性或方法绑定到原型上。
实例对象想访问到原型对象,可以使用__proto__属性。
任何对象都有一个属性叫做__proto__ 这个属性可以访问到对应的构造函数的原型对象,也就是构造函数的prototype属性的值;
var arr = [1,2,3];
arr.__proto__.eat = function(){
console.log("能吃");
}
console.log(arr);
console.log(arr.__proto__);
构造器:
示例:
function Person(){
}
var obj1 = new Person();
console.log(obj1.__proto__.constructor === Person); // true
console.log(obj1.constructor === Person);
// true obj1对象中没有constructor属性,所以去原型对象上找
原型链:
function Person(){
}
var obj = new Person();
var proto = obj.__proto__;
console.log(proto);
继续访问原型对象的原型:
function Person(){
}
var obj = new Person(); // 通过构造函数创建了对象obj
var proto = obj.__proto__; // 通过实例对象访问到原型对象
var proto1 = proto.__proto__; // 访问原型对象的元素
console.log(proto1);
其实这个对象,就是Object构造函数的原型对象。
那么这个对象的原型是什么:
function Person(){
}
var obj = new Person(); // 通过构造函数创建了对象obj
var proto = obj.__proto__; // 通过实例对象访问到原型对象
var proto1 = proto.__proto__; // 访问原型对象的元素
var proto2 = proto1.__proto__; // 访问Object的原型对象的原型
console.log(proto);
console.log(proto1);
最后访问到null,也就是说到头了,这是最顶级的对象
通过一张图来将上面几个对象的关系画出来:
这样向上的一条链式结构,我们称之为原型链。
对象查找属性的规则:
先在自己身上找,如果有,直接使用,如果没有,顺着原型链往上找,找到了就使用,找不到就继续往上找,如果找到了null,都没有的话,就返回undefined;但是对象属性赋值和原型没关系,有就修改,没有就增加。