why-js-day5 原型

  • Post author:
  • Post category:其他


[[prototype]]

JavaScript对象都有一个[[prototype]]的内置属性,指向其他对象,几乎所有对象在创建时该属性都会被赋予非空的值,但可以为空

let myobj={
    a:2
};
console.log(myobj.a);

当试图引用对象的属性时会触发[[Get]]操作,对于默认的[[Get]]操作

  1. 检查对象本身是否有这个属性,有就使用
  2. 如果没有则继续访问对象的[[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]]会被遍历,如果原型链上依旧没有访问属性,则属性会被直接添加到对象上
  • 如果对象本身和原型链上都存在所访问属性,则会发生遮蔽,对象本身属性会遮蔽所有原型链上的属性

属性不直接存在于对象而是存在于原型链上层时

  1. 如果原型链上层存在普通访问属性(foo),并且writable:true,则会直接在对象上添加新属性,为遮蔽属性
  2. 如果原型链上层存在,且被标记为writable:false,则无法修改会在对象上创建遮蔽属性,严格模式下会报错,否则被忽略,不发生遮蔽
  3. 如果原型链上层存在,且为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();



版权声明:本文为qq_38904449原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。