(本文摘录于阮一峰老师所写文章,https://wangdoc.com/javascript/basic/index.html,作个人学习使用。)
prototype 属性的作用
1、JavaScript 规定,每个函数都有一个prototype属性,指向一个对象。对于普通函数来说,该属性基本无用。但是,对于构造函数来说,生成实例的时候,该属性会自动成为实例对象的原型。原型对象的属性不是实例对象自身的属性。只要修改原型对象,变动就立刻会体现在所有实例对象上。
2、当实例对象本身没有某个属性或方法的时候,它会到原型对象去寻找该属性或方法。这就是原型对象的特殊之处。
如果实例对象自身就有某个属性或方法,它就不会再去原型对象寻找这个属性或方法。
原型链
3、所有对象的原型最终都可以上溯到Object.prototype,即Object构造函数的prototype属性。也就是说,所有对象都继承了Object.prototype的属性。这就是所有对象都有valueOf和toString方法的原因。
而Object.prototype的原型是null。null没有任何属性和方法,也没有自己的原型。因此,原型链的尽头就是null。
4、constructor 属性:
prototype对象有一个constructor属性,默认指向prototype对象所在的构造函数。
实例对象自身没有constructor属性,该属性其实是读取原型链上面的 构造函数.prototype.constructor属性。(P.prototype.constructor===P)
constructor属性的作用是,可以得知某个实例对象,到底是哪一个构造函数产生的。
同时,有了constructor属性,就可以从一个实例对象新建另一个实例。比如new x.constructor()
修改原型对象时,一般要同时修改constructor属性的指向。注意,普通对象的constructor属性指向Object构造函数。
用constructor.name属性可以从实例获得构造函数名。
5、instanceof运算符:instanceof运算符返回一个布尔值,表示对象是否为某个构造函数的实例。
instanceof运算符的左边是实例对象,右边是构造函数。它会检查右边构建函数的原型对象(prototype),是否在左边对象的原型链上。因此,下面两种写法是等价的。
v instanceof Vehicle
// 等同于
Vehicle.prototype.isPrototypeOf(v)
由于任意对象(除了null)都是Object的实例,所以instanceof运算符可以判断一个值是否为非null的对象。
6、构造函数的继承:
第一步是在子类的构造函数中,调用父类的构造函数。
function Sub(value) {
Super.call(this);
this.prop = value;
} //Super是父类的构造函数,this指的是子类的实例,这个调用可以让子类实例拥有父类实例的属性
第二步,是让子类的原型指向父类的原型,这样子类就可以继承父类原型。
Sub.prototype = Object.create(Super.prototype); //将父类的原型赋值给子类的原型,不能直接等于,不然会影响后续两步的操作
Sub.prototype.constructor = Sub;
Sub.prototype.method = '...';
7、多重继承
JavaScript 不提供多重继承功能,即不允许一个对象同时继承多个对象。但是,可以通过变通方法,实现这个功能。
// 继承 M1
S.prototype = Object.create(M1.prototype);
// 继承链上加入 M2
Object.assign(S.prototype, M2.prototype);
// 指定构造函数
S.prototype.constructor = S;
子类S同时继承了父类M1和M2。这种模式又称为 Mixin(混入)。
模块
8、模块是实现特定功能的一组属性和方法的封装。一种做法是使用“立即执行函数”(Immediately-Invoked Function Expression,IIFE),将相关的属性和方法封装在一个函数作用域里面,可以达到不暴露私有成员的目的。
(还不太理解,以后慢慢了解)