本文已参与「新人创作礼」活动,一起开启掘金创作之路。
自我介绍:大家好,我是吉帅振的网络日志;微信公众号:吉帅振的网络日志;前端开发工程师,工作4年,去过上海、北京,经历创业公司,进过大厂,现在郑州敲代码。
JS继承专栏
一、构造函数
js中定义一个function,当使用new关键字调用这个function的时候,这个function成为一个构造函数。如下所示:
function Person(name) {
this.name = name;
}
// 使用new关键字调用Person函数,Person函数成为构造函数
var xiaoming = new Person();
二、new关键字
当使用new关键字调用构造函数生成一个对象实例时,js做出的关键处理如下:
1.创建一个新的对象,将构造函数内this指针指向新建对象。
2.将新建对象的__proto__属性设置成构造函数的prototype属性,确保新建对象是构造函数实例。
3.运行一遍构造函数,如果构造函数本身需要返回一个object或者array对象,则舍去新创建对象,返回构造函数需要返回的对象;否则返回新建对象
/**
* @bug 这里假设new出来实例的都是object,不是array对象
* @description 传入构造函数,以及构造函数参数,模拟构造函数返回一个新建实例
* @param {function} constructor 需要传入的构造函数
* @param {any} params 跟在constructor参数后面的所有参数,用来模拟传入constructor的参数
* @returns 返回一个新的对象
*/
function mockNew(constructor) {
// 新建一个对象
var obj = {};
// 修改对象的__proto__ 确保使用instanceof关键字时没有问题
obj.__proto__ = constructor.prototype;
// 将构造函数的this指针绑定到obj,然后调用
var params = Array.prototype.splice.call(arguments, 1);
var result = constructor.apply(obj, params);
// 判断构造函数是否返回一个对象,是的话直接返回构造函数返回的对象
if (typeof result === 'object' || typeof result === 'array') {
return result;
}
return obj;
}
// 定义两个构造函数,分别使用mockNew调用以及使用new关键字调用
function Person(name, age) {
this.name = name;
this.age = age;
}
function Person2(name, age) {
this.name = name;
this.age = age;
var result = {};
return result;
}
// 使用mock函数
console.log('============ xiaoming start ==============');
var xiaoming = mockNew(Person, 'xiaoming', 26);
console.log(xiaoming);
console.log(xiaoming instanceof Person);
console.log('============ xiaohong start ==============');
var xiaohong = mockNew(Person2, 'xiaohong', 26);
console.log(xiaohong);
console.log(xiaohong instanceof Person2);
// 使用new关键字
console.log('============ xiaoqiao start ==============');
var xiaoqiao = new Person('xiaoqiao', 26);
console.log(xiaoqiao);
console.log(xiaoqiao instanceof Person);
console.log('============ xiaobing start ==============');
var xiaobing = new Person2('xiaobing', 26);
console.log(xiaobing);
console.log(xiaobing instanceof Person2);
输出截图如下:
三、基本思想
构造函数继承的基本思想就是利用call或者apply把父类中通过this指定的属性和方法复制(借用)到子类创建的实例中。因为this对象是在运行时基于函数的执行环境绑定的。在全局中,this等于window,而当函数被作为某个对象的方法调用时,this等于那个对象。call 、apply方法可以用来代替另一个对象调用一个方法。call、apply 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。
new对象的时候(注意,new操作符与直接调用是不同的,以函数的方式直接调用的时候,this指向window,new创建的时候,this指向创建的这个实例),创建了一个新的实例对象,并且执行SubType里面的代码,而SubType里面用call调用了SuperTyep,也就是说把this指向改成了指向新的实例,所以就会把SuperType里面的this相关属性和方法赋值到新的实例上,而不是赋值到SupType上面。所有实例中就拥有了父类定义的这些this的属性和方法。
function SuperType(){
this.colors = ["red", "blue", "green"];
}
function SubType(){
//继承了SuperType
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green"