JavaScript中的对象概念和其他语言的对象概念有些不一样,所以学习起来稍微有点特殊。
-
在JavaScript中,构造函数的使用一般如下:
/* function MyConstructor(){ // 函数实体写在这里 // 根据需要在this上创建属性,然后赋值给它们,比如: this.a = 123; // 等等... // 如果函数具有返回对象的return语句, // 则该对象将是 new 表达式的结果。 // 否则,表达式的结果是当前绑定到 this 的对象。 //(即通常看到的常见情况,其实就是在调用函数)。 } */ function C(){ this.a = 37; } var o = new C(); console.log(o.a); //会输出 37 function C2(){ this.a = 37; return {a:38}; } o = new C2(); console.log(o.a); //会输出 38
-
所谓的“构造函数”,其实本质上还是一个普通的函数,但是由于在内部使用了
this
变量,如果对构造函数使用new
运算符,就能够生成一个实例化的对象出来,并且在构造函数里面所使用到的this
变量或方法,都会绑定到这个实例对象上面来。如果此时写一个人的构造函数,可以简单这么写:
function Person(name, age, sex){ this.name = name; this.age = age; this.sex = sex; }
实例化一个人的对象出来的话,可以这么来使用:
var zhangsan = new Person("zhangsan", 18, "男"); var lisi = new Person("lisi", 19, "男"); console.log(typeof zhangsan); //object console.log(zhangsan); //Person { name: 'zhangsan', color: 18, sex: '男' } console.log(typeof lisi); //object console.log(lisi); //Person { name: 'lisi', color: 19, sex: '男' }
经过上面的操作,就会构造两个
Person
的对象zhangsan
和lisi
出来,这两个对象都自动会含有一个constructor
属性,指向它们的构造函数;也可以通过instanceof
运算符来验证实例对象和原型对象的关系:console.log(zhangsan.constructor === Person);//True console.log(lisi.constructor === Person);//True console.log(zhangsan instanceof Person);//True console.log(lisi instanceof Person);//True
-
构造函数的
prototype
属性,每个构造函数都拥有一个protorype
属性,能够指向另外一个对象,这个对象的所有属性和方法,都会被构造函数的实例所继承,这样就可以把对象的一些通用不变的属性和方法直接定义在prototype
对象上:function Person(name, age, sex){ this.name = name; this.age = age; this.sex = sex; } Person.prototype.species = "人类"; Person.prototype.eat = function () { console.log("吃东西"); } var zhangsan = new Person("zhangsan", 18, "男"); console.log(zhangsan.species); //人类 zhangsan.eat(); //吃东西
这个时候所有实例化对象的
type
属性和eat()
方法,其实都是指向prototype
对象。 -
可以使用
isPrototypeOf()
方法来判断某个proptotype
对象和某个实例之间的关系:console.log(Person.prototype.isPrototypeOf(zhangsan)); //True
-
可以使用
hasOwnProperty()
方法来判断对象的某个属性是本身属性还是proptotype
对象的属性:console.log(zhangsan.hasOwnProperty("sex")); //True console.log(zhangsan.hasOwnProperty("species")); //False
-
JavaScript
中的in
运算符可以用来判断实例化对象是否含有某个属性[无论是不是本身的属性],也可以用来遍历某个对象的所有属性。
构造函数的继承的五种方法
此时这里有两个构造函数,Person
和Developer
,如下:
function Person(){
this.species = "人类";
}
function Developer(name, age, sex){
this.name = name;
this.age = age;
this.sex = sex;
}
-
使用
call()
或者apply()
的方法,将父对象的构造函数绑定在子对象上面,实现如下:function Developer(name, age, sex){ //使用apply方法 Person.apply(this, arguments); //或者使用call方法 //Person.call(this, name, age, sex); this.name = name; this.age = age; this.sex = sex; } var developer_1 = new Developer("zhangsan", 20, "男"); console.log(developer_1); //Developer { species: '人类', name: 'zhangsan', age: 20, sex: '男' }
-
使用
prototype
属性:function Person(){ this.species = "人类"; } function Developer(name, age, sex){ this.name = name; this.age = age; this.sex = sex; } Developer.prototype = new Person(); Developer.prototype.constructor = Developer; var developer_1 = new Developer("zhangsan", 20, "男"); console.log(developer_1); // Developer { name: 'zhangsan', age: 20, sex: '男' } console.log(developer_1.species); // 人类
Developer.prototype = new Person();
就相当于给构造函数Developer
的prototype
对象重新赋了一个新的值;又由于任何一个prototype
对象都有一个constructor
属性,指向它的构造函数的。而且每个实例的constructor
属性,默认调用prototype
对象的constructor
属性。因此此时
Developer
的prototype
对象的constructor
属性就是指向Person
的,这个时候加上Developer.prototype.constructor = Developer;
,会让Developer
的prototype
对象的constructor
属性就是指向Developer
。 -
直接继承
prototype
这种方式是将不变的父类的属性直接写入到它的
prototype
上面,此时直接将父类的protptype
属性赋值给子类的prototype
,然后再更改子类的prototype
的constructor
属性指向即可,这种方式的优点是效率较高[不用执行和建立Person
的实例了],缺点是两个构造函数的prototype
属性是指向同一个对象的,那么任何修改都会映射到两个上面:function Person() { } Person.prototype.species = "人类"; function Developer(name, age, sex){ this.name = name; this.age = age; this.sex = sex; } Developer.prototype = Person.prototype; Developer.prototype.constructor = Developer; var developer_1 = new Developer("zhangsan", 20, "男"); console.log(developer_1); // Developer { name: 'zhangsan', age: 20, sex: '男' } console.log(developer_1.species); // 人类 // 缺点就是两个prototype会指向同一个 console.log(Developer.prototype.constructor); // [Function: Developer] console.log(Person.prototype.constructor); // [Function: Developer]
-
在直接继承
prototype
的基础上,可以再利用一个空对象做中介因为直接继承
prototype
这种方式仍有一些缺点,所以此时可以这么做:function Person() { } Person.prototype.species = "人类"; function Developer(name, age, sex){ this.name = name; this.age = age; this.sex = sex; } var F = function(){}; F.prototype = Person.prototype; Developer.prototype = new F(); Developer.prototype.constructor = Developer;
这样对
Developer
的prototype
对象的修改,就不会影响到Person
,而是修改到了F
上面,此时还可以继续改进,将上面的代码封装到一个函数里面,更加方便使用:function packaging(Child, Parent) { var F = function () { }; F.prototype = Parent.prototype; Child.prototype = new F(); Child.prototype.constructor = Child; //uber属性:指向父类原型。 Child.uber = Parent.prototype; } packaging(Developer, Person) var developer_1 = new Developer("zhangsan", 20, "男") console.log(developer_1) // Developer { name: 'zhangsan', age: 20, sex: '男' }
-
拷贝继承
如果纯粹采用"拷贝"方法实现继承。简单说,如果把父对象的所有属性和方法,拷贝进子对象,也能够实现继承,具体如下:
function Person() { } Person.prototype.species = "人类"; function Developer(name, age, sex){ this.name = name; this.age = age; this.sex = sex; } function copy_inherit(Child, Parent) { var parent = Parent.prototype; var child = Child.prototype; for (var attribute in parent){ child[attribute] = parent[attribute]; } child.uber = parent; } copy_inherit(Developer, Person) var developer_1 = new Developer("zhangsan", 20, "男") console.log(developer_1) //Developer { name: 'zhangsan', age: 20, sex: '男' } console.log(developer_1.species)// 人类