原因
我们知道,对象是由构造函数产生的,构造函数可以视为对象的模板。
有一个缺点,同一个构造函数生成的多个实例之间无法共享属性,从而造成浪费
function Cat(name,color){
this.name=name;
this.color=color;
this.meow=function(){
console.log("hi");
}
}
var cat1 = new Cat('cat1','white');
var cat2 = new Cat('cat2','black');
cat1.meow===cat2.meow //false
因为每生成一个实例对象,就会新生成一次meow函数。这样没有必要,而且浪费资源,应该共享。
prototype 原型对象
JavaScript 继承机制的设计思想就是,原型对象的所有属性和方法,都能被实例对象共享。
每个函数都有一个prototype指向一个对象。
逻辑:
构造函数的prototype属性,就是它们实例对象的原型对象。
属性共享:
function Animal(name){
this.name=name;
}
Animal.prototype.color='white';
var cat1=new Animal('cat1');
var cat2=new Animal('cat2');
cat1.color //white
cat2.color //white
因为原型对象的属性不是实例自身的属性,所以原型对象只要改变了属性,它的所有实例都会改变。比如以上Animal.prototype.color=‘yellow’,则cat1和cat2都会变成yellow
原型对象的作用,就是定义所有实例对象共享的属性和方法。实例对象可以视作从原型对象衍生出来的子对象。
如果实例对象自身就有某个属性或方法,它就不会再去原型对象寻找这个属性或方法。
原型链
所有对象都有自己的原型对象(prototype)。
任何一个对象,都可以充当其他对象的原型,而它也有自己的原型,形成了一个原型链。
所有对象的原型最终都上溯到Object.prototype,即Object构造函数的prototype属性。所有对象都继承了Object.prototype的属性。这就是所有对象都有valueOf和toString方法的原因,因为这是从Object.prototype继承的。
Object.prototype的原型是null,null没有任何属性和方法,也没有自己的原型,所以原型链的尽头是null。读取对象的某个属性时,JavaScript 引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。如果直到最顶层的Object.prototype还是找不到,则返回undefined。如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”(overriding)。
这样对性能是有印象的,如果寻找某个不存在的属性,会遍历整个原型链。
constructor属性
prototype对象有一个constructor属性,默认指向prototype对象所在的构造函数。
function P() {}
var p = new P();
p.constructor === P // true
p.constructor === P.prototype.constructor // true
p.hasOwnProperty('constructor') // false
用它可以得知某个实例对象是哪个构造函数产生的。
可以从一个实例对象新建另一个实例。
function Constr() {}
var x = new Constr();
var y = new x.constructor();
y instanceof Constr // true
如果更改了原型,同时也会更改constructor。
function Person(name) {
this.name = name;
}
Person.prototype.constructor === Person // true
Person.prototype = { //变成了一个普通函数,构造函数为Object
method: function () {}
};
Person.prototype.constructor === Person // false
Person.prototype.constructor === Object // true
所以修改对象通常也要修改constructor指向。
f.constructor.name //‘Foo’ 查看构造函数名
instanceof 运算符
instanceof运算符返回一个布尔值,表示对象是否为某个构造函数的实例。
v instanceof Vehicle //true
// 等同于
Vehicle.prototype.isPrototypeOf(v)
由于任意对象(除了null)都是Object的实例,所以instanceof运算符可以判断一个值是否为非null的对象。
null instanceof Object //false
instanceof的原理是检查右边构造函数的prototype属性,是否在左边对象的原型链上。有一种特殊情况,就是左边对象的原型链上,只有null对象。这时,instanceof判断会失真。
var obj = Object.create(null);
typeof obj // "object"
Object.create(null) instanceof Object // false
左边的原型是null 而null不是Object
instanceof也可以用来判断值类型。只能用于对象,不能用于原始类型。
利用instanceof运算符,还可以巧妙地解决,调用构造函数时,忘了加new命令的问题。
function Fubar (foo, bar) {
if (this instanceof Fubar) {
this._foo = foo;
this._bar = bar;
} else {
return new Fubar(foo, bar);
}
}
构造函数的继承
第一步,在子类的构造函数中调用父类的构造函数
function Sub(value){ //子类构造函数
Super.call(this); //子类实例,父类构造函数Super,父类属性prop
this.prop=value;
}
第二步,子类的原型指向父类原型
Sub.prototype=Object.create(Super.prototype);//子类原型由父类原型创造,这样就不会更改时把父类原型也改掉
Sub.prototype.constructor=Sub; //必须要
Sub.prototype.method='';
写法二:
Sub.prototype= new Super; //但此时子类会有父类的实例方法
instanceof会对子类父类的构造函数都返回true。
单个继承:
ClassB.prototype.print = function() {
ClassA.prototype.print.call(this);
// some code
}
多重继承
js不提供这个功能,即不允许一个对象同时继承多个对象,但我们可以变通实现
function M1(){
this.hello='hello';
}
function M2(){
this.world='world';
}
function S(){
M1.call(this);
M2.call(this);
}
//1.先继承M1
S.prototype=Object.create(M1.prototype);
//2.继承链加上M2
Object.assign(S.prototype,M2.prototype); //assign函数将一个对象复制到另一个上,返回目标对象。
//3.指定构造函数
S.prototype.constructor=S;
var s=new S();
s.hello//'hello'
s.world//'world'
这种模式又称为混入(Mixin)
模块
模块是实现特定功能的一组属性和方法的封装
简单的做法是把模块写成一个对象,所有的模块成员都放到这个对象里面。
几个概念:
1.封装私有变量:构造函数写法
2.封装私有变量:立即执行函数写法;不暴露私有成员
3.模块放大模式 4.输入全局变量。
77

被折叠的 条评论
为什么被折叠?



