「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」。
1.什么是构造函数
所谓"构造函数",其实就是一个普通函数,但是内部使用了 this 变量。
1.1.普通函数
普通函数的定义:
function Cat(name,color) {
return {
name:name,
color:color
}
}
普通函数的调用:生成实例对象,等于是在调用函数。
var cat1 = Cat("大毛","黄色");
var cat2 = Cat("二毛","黑色");
console.log(cat1)
console.log(cat2)
存在的问题:cat1 和 cat2 之间没有内在的联系,不能反映出它们是同一个原型对象的实例。
1.2.构造函数
构造函数的定义:使用 this。
function Cat(name,color){
this.name=name;
this.color=color;
}
构造函数的调用:生成实例对象,使用 new。
var cat1 = new Cat("大毛","黄色");
var cat2 = new Cat("二毛","黑色");
console.log(cat1)
console.log(cat2)
这时 cat1 和 cat2 会自动含有一个 constructor 属性,指向它们的构造函数。
console.log(cat1.constructor == Cat); //true
console.log(cat2.constructor == Cat); //true
Javascript 还提供了一个 instanceof 运算符,验证原型对象与实例对象之间的关系。
console.log(cat1 instanceof Cat); //true
console.log(cat2 instanceof Cat); //true
构造函数的缺点:构造函数方法很好用,但是存在一个浪费内存的问题。
2.构造函数的继承
已知 Animal 对象的构造函数及 Cat 对象的构造函数,如何使 Cat 继承 Animal 的方法及属性。
2.1.通过构造函数绑定进行实现
已知 Animal 如下:
function Animal() {
this.species = "动物";
}
1)apply()
function Cat_apply(name, color) {
Animal.apply(this, arguments);// Animal中的属性及方法被应用到Cat_apply中
console.log(this,arguments);// this: Cat_apply; arguments: 其他参数(数组形式)
this.name = name;
this.color = color;
}
var cat1 = new Cat_apply("爆爆", "三花");
console.log("apply方法:" + cat1.species +":" + cat1.name + ":" + cat1.color);
由控制台输出可知,Cat_apply 继承了 Animal 的属性 species。
2)call()
function Cat_call(name, color) {
Animal.call(this, arguments);// Animal中的属性及方法被应用到Cat_call中
console.log(this,arguments);// this: Cat_call; arguments: 其他参数(逗号隔开)
this.name = name;
this.color = color;
}
var cat2 = new Cat_call("皮皮", "蓝猫");
console.log("call方法:" + cat2.species +":" + cat2.name + ":" + cat2.color);
输出结果如下。
2.2.通过 prototype 模式实现
已知 Animal 和 Cat 如下:
function Animal() {
this.species = "动物";
}
function Cat(name, color) {
this.name = name;
this.color = color;
this.eat = function(){alert("吃老鼠");};
}
新建Animal的实例,并让 Cat 的prototype对象指向它,这样所有 Cat 的实例也就能继承 Animal 了。
Cat.prototype = new Animal();
因为 Cat 的 prototype 对象指向了 Animal 的实例,这就会导致 Cat.prototype.constructor 自动指向 Animal,而 Cat 的每一个实例也会指向Animal,很明显这会导致继承链紊乱(Cat 的实例明明是构造函数 Cat 生成的),应该指向 Cat,而不是指向 Animal, 所以我们需要手动更正一下,将 Cat.prototype.constructor 手动指向 Cat。
Cat.prototype.constructor = Cat;
然后再进行调用。
var cat3 = new Cat("小宝妹", "银渐层");
console.log("prototype模式:" + cat3.species +":" + cat3.name + ":" + cat3.color); // 动物
输出结果。
2.3.直接继承 prototype
该方法是第二种方法的改进。
由于对于不变的属性都可以直接写入 Animal.prototype,所以,我们也可以直接跳过 Animal(),直接继承 Animal.prototype。
改写Animal对象:
function Animal() {};
Animal.prototype.species = "动物";
function Cat(name, color) {
this.name = name;
this.color = color;
this.eat = function(){alert("吃老鼠");};
}
让 Cat 的prototype 直接指向 Animal 的 prototype,这样会存在一个问题就是:Cat.prototype改变,Animal.prototype也会更改。
Cat.prototype = Animal.prototype;
很明显这也会导致继承链紊乱,也所以我们需要手动更正一下,将 Cat.prototype.constructor 手动指向 Cat。
Cat.prototype.constructor = Cat;
然后再进行调用。
var cat4 = new Cat("雪碧", "高地");
console.log('直接继承prototype:' + cat4.species +":" + cat4.name + ":" + cat4.color);// 动物
输出结果如下。
2.4.利用空对象作为中介
已知 Animal 和 Cat 如下:
function Animal() {};
Animal.prototype.species = "动物";
function Cat(name, color) {
this.name = name;
this.color = color;
this.eat = function(){alert("吃老鼠");};
}
先定义一个空的函数对象。
var F = function () {};
让空对象的 prototype 指向 Animal 的 prototype。
F.prototype = Animal.prototype;
console.log(F.prototype == Animal.prototype)//true
再让 Cat 的 prototype 指向该空对象的实例对象。
Cat.prototype = new F();
将 Cat.prototype.constructor 手动指向 Cat,防止继承链紊乱。
Cat.prototype.constructor = Cat;
最后调用。
var cat5 = new Cat("大毛", "黄色");
console.log('利用空对象作为中介1:' + cat5.species +":" + cat5.name + ":" + cat5.color);
输出结果如下。
可以将上面的方法封装成一个函数,方便以后调用。封装函数如下:
function extend(Child, Parent) {
var F = function () {};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
Child.uber = Parent.prototype;
}
使用,前提是已知Animal 和 Cat, 如下:
function Animal() {};
Animal.prototype.species = "动物";
function Cat(name, color) {
this.name = name;
this.color = color;
this.eat = function(){alert("吃老鼠");};
}
调用封装的方法实现继承
extend(Cat, Animal);
var cat6 = new Cat("二毛", "黄色");
console.log('利用空对象作为中介2:' + cat6.species +":" + cat6.name + ":" + cat6.color);
输出结果如下。
2.5.拷贝继承
把父对象的所有属性和方法,拷贝进子对象。
function Animal() {};
Animal.prototype.species = "动物";
function Cat(name, color) {
this.name = name;
this.color = color;
this.eat = function(){alert("吃老鼠");};
}
写一个实现属性拷贝的函数
function extend2(Child, Parent) {
var p = Parent.prototype;
var c = Child.prototype;
or (var i in p) {
c[i] = p[i];
}
c.uber = p;
}
使用
extend2(Cat, Animal);
var cat7 = new Cat("三毛", "黄色");
console.log('拷贝继承:' + cat7.species +":" + cat7.name + ":" + cat7.color);//动物
输出结果如下。