[[prototype]]
JavaScript对象都有一个[[prototype]]的内置属性,指向其他对象,几乎所有对象在创建时该属性都会被赋予非空的值,但可以为空
let myobj={
a:2
};
console.log(myobj.a);
当试图引用对象的属性时会触发[[Get]]操作,对于默认的[[Get]]操作
- 检查对象本身是否有这个属性,有就使用
- 如果没有则继续访问对象的[[prototype]]链
Object,create(…)会创建一个对象并把对象的[[prototype]]关联到指定对象
let anotherObject={
a:2
};
let myobj=Object.create(anotherObject);
console.log(myobj.a);
in操作符可以检查属性在对象是否存在,同样会查找整条原型链(无论是否可枚举),for…..in同样但属性是可枚举
let anotherobj={
a:2
};
let myobj=Object.create(anotherobj);
for (let k in myobj){
console.log("found: "+k);
}
console.log('a' in myobj);
[[prototype]]尽头,所有的[[prototype]]最终都会指向内置的Object.prototype,所有普通对象都源于这个对象,所以它包含了很多常用功能
属性设置和屏蔽
let myobj={};
myobj.foo='bar';
- 如果对象本身(myobj)包含普通的数据访问属性(foo),赋值语句只会修改已有的属性值
- 如果对象本身(myobj)不存在访问属性(foo),则[[prototype]]会被遍历,如果原型链上依旧没有访问属性,则属性会被直接添加到对象上
- 如果对象本身和原型链上都存在所访问属性,则会发生遮蔽,对象本身属性会遮蔽所有原型链上的属性
属性不直接存在于对象而是存在于原型链上层时
- 如果原型链上层存在普通访问属性(foo),并且writable:true,则会直接在对象上添加新属性,为遮蔽属性
- 如果原型链上层存在,且被标记为writable:false,则无法修改会在对象上创建遮蔽属性,严格模式下会报错,否则被忽略,不发生遮蔽
- 如果原型链上层存在,且为setter,则会调用setter,属性不会被添加到对象上,也不会重新定义setter
如果希望第2,3种情况下也能产生屏蔽,则不能用=操作符,而是使用Object.defineProperty(…)
let mypro={};
Object.defineProperty(mypro,'foo',{
value:2,
writable:false
});
let myobj=Object.create(mypro);
myobj.foo=3;
console.log(myobj.foo);
console.log(myobj.hasOwnProperty('foo'));
Object.defineProperty(myobj,'foo',{
value:3
});
console.log(myobj.foo);
console.log(myobj.hasOwnProperty('foo'));
隐式屏蔽
let anobj={
a:2
};
let myobj=Object.create(anobj);
console.log(anobj.a,myobj.a,anobj.hasOwnProperty('a'),myobj.hasOwnProperty('a'));
myobj.a++;
console.log(anobj.a,myobj.a,myobj.hasOwnProperty('a'));
类
JavaScript中并没有类
所有函数都会默认拥有一个名为prototype的公有且不可枚举的属性,会指向另一个对象
function Foo() {}
let obj=new Foo();
console.log(Foo.prototype===Object.getPrototypeOf(obj),Foo.prototype===obj.__proto__);
JavaScript中只能创建多个对象,[[prototype]]关联在同一对象,new Foo()这个函数调用实际并没有直接创建关联,只是一个副作用,直接创建可用Object.create(…)
原型对象默认有一个公共且不可枚举的.constructor属性,指向创建这个对象的函数,实例对象本身并没有.constructor这个属性,而是委托给原型对象,原型对象的.constructor只是函数在声明时的默认属性,用新对象替换原型对象后并不会自动获取.constructor属性
function Foo() {}
let a=new Foo();
console.log(Foo.prototype.constructor === Foo,a.constructor===Foo);
console.log(a.hasOwnProperty('constructor'));
function Foo() {}
Foo.prototype={};
let a1=new Foo();
console.log(a1.constructor===Foo,a1.constructor===Object);
手动添加.constructor属性
function Foo() {}
Foo.prototype={};
Object.defineProperty(Foo.prototype,'constructor',{
enumerable:false,
writable:true,
configurable:true,
value:Foo
});
let a1=new Foo();
console.log(a1.constructor===Foo,a1.constructor===Object);
JavaScript并没有构造函数.只有对函数的构造调用
function Foo() {
console.log('laji');
}
let a=new Foo();
Foo();
原型继承(ES6的设置原型方法.Object.setPrototypeof(obj,objproto))
function Foo(name) {
this.name=name;
}
Foo.prototype.myname=function () {
return this.name;
};
function Bar(name,label) {
Foo.call(this,name);
this.label=label;
}
Bar.prototype=Object.create(Foo.prototype);
//Object.setPrototypeOf(Bar.prototype,Foo.prototype);
Bar.prototype.mylabel=function () {
return this.label;
};
let a=new Bar('a','obj a');
console.log(a.myname(),a.mylabel());
检查类关系
内省,instanceof左操作数是一个对象,右操作数是一个函数,返回值是在对象的整条[[prototype]]链中是否有指向函数原型的对象
无法判断两个对象之间通过[[prototype]]关联
function Foo() {}
let a=new Foo();
console.log(a instanceof Foo);
使用内置的bind(…)所生成的硬绑定函数没有.prototype属性,在该函数使用instanceof目标函数的.prototype会代替绑定函数的.prototype
function foo(something) {
console.log(this.a,something);
return this.a+something;
}
let obj={
a:2
};
console.log(foo.prototype);
let bar=foo.bind(obj);
let b=bar(3);
console.log(b);
console.log(foo.prototype,bar.prototype);
第二种判断内省的方法,isPrototypeof(…)在a的整条原型链中是否存在Foo.prototype
function Foo() {}
let a=new Foo();
console.log(Foo.prototype.isPrototypeOf(a));
直接获取一个对象的[[prototype]]链
Object.getPrototypeof(a);
console.log(Object.getPrototypeOf(a)===Foo.prototype);
浏览器也支持一种非标准的方法访问[[prototype]],与.constructor一样,.__proto__实际存在于内置的Object.prototype中
function Foo() {}
let a=new Foo();
console.log(a.hasOwnProperty('__proto__'),a.__proto__===Foo.prototype);
.__proto__看起来像属性但更像getter/setter
Object.defineProperty(Object.prototype,"__proto__",{
get:function () {
return Object.getPrototypeOf(this);
},
set:function (o) {
Object.setPrototypeOf(this,o);
return o;
}
});
对象关联
Object.create(…)会创建一个新对象并关联到指定对象上,并且不会像使用new的构造函数调用生成.prototype和.constructor引用,
Object.create(null)会创建一个空[[prototype]]链的对象,这样空[[prototype]]对象被称为字典,非常适合储存数据
let foo={
something:function () {
console.log("tell me something good...");
}
};
let bar=Object.create(foo);
bar.something();
Object.create(…)的polyfill代码
if (!Object.create){
Object.create=function (o) {
function F() {}
F.prototype=o;
return new F();
}
}
附加功能
let anobj={a:2};
let myobj=Object.create(anobj,{
b:{
enumerable:false,
writable:true,
configurable:false,
value:3
},
c:{
enumerable:true,
writable:false,
configurable:false,
value:4
}
});
console.log(myobj.hasOwnProperty('a'),myobj.hasOwnProperty('b'),myobj.hasOwnProperty('c'));
console.log(myobj.a,myobj.b,myobj.c);
关联关系是备用
直接委托
let anobj={
cool:function () {
console.log("cool");
}
};
let myobj=Object.create(anobj);
myobj.cool();
委托设计,通过内部委托,比直接委托可使API设计更加清晰
let anobj={
cool:function () {
console.log("cool");
}
};
let myobj=Object.create(anobj);
myobj.docool=function () {
this.cool();
};
myobj.docool();