JavaScript的对象和原型链

首先了解一下创建对象的几种方式,介绍以下三种。

// 第一种方式:字面量
var o1 = {name: 'o1'};
var o2 = new Object({name: 'o2'});

// 第二种方式:构造函数
var M = function (name) { this.name = name; }
var o3 = new M('o3');

// 第三种方式:Object.create 
var p = {name: 'p'};
var o4 = Object.create(p);
原型链1.png

在上面的例子中o3就是实例,M就是构造函数。从上图中可以知道,实例的__protpo__指向的是构造函数的原型对象。每个对象都有 __proto__ 属性,但只有函数对象才有prototype属性。

使用Object.create()创建关联

var foo = {
    something: function() {
        console.log('ok');
    }
};

var bar = Object.create( foo );
bar.something(); // ok
console.log(bar.__proto__ === foo) // true

Object.create()是ES5新增的函数。Object.create()方法接受两个参数:Object.create(obj,propertiesObject) 。
obj:一个对象,是新创建的对象的原型。
propertiesObject:该参数对象是一组属性与值,该对象的属性名称将是新创建的对象的属性名称,可选。

var o = Object.create(Object.prototype, {
// foo会成为所创建对象的数据属性
foo: { 
    writable:true,
    configurable:true,
    value: "hello" 
}}),

console.log(o);//{foo:'hello'}

Object.create(null) 创建的对象是一个空对象,在该对象上没有继承 Object.prototype 原型链上的属性或者方法,例如:toString(), hasOwnProperty()等方法。Object.create(null) 适合用来存储字典数据。

var test1 = Object.create(null) ;
console.log(test1);// {} No Properties 

Object.create()的polyfill代码

if(!Object.create) {
    Object.create = function(o) {
        function F() {}
        F.prototype = o;
        return new F();
    }
}

什么是原型链?

简单理解就是原型组成的链,实例的__proto__是构造函数的原型,而原型也是一个对象,也有__proto__属性,就这样可以一直通过__proto__向上找,这就是原型链,当最终找到Object的原型的时候,这条原型链就算到头了。

function Parent() {}

function Child() {}
Child.prototype = new Parent();

var c = new Child();

console.log(c.__proto__ === Child.prototype)   // true
console.log(Child.prototype.__proto__ === Parent.prototype)   // true
console.log(Parent.prototype.__proto__ === Object.prototype)  // true
console.log(Object.prototype.__proto__)  // null

使用for...in遍历对象时原理和原型链类似,任何可以通过原型链访问到(enumerable)的属性都会被枚举。

var anotherObj = { a:2 };
// 创建一个关联到anotherObj的对象
var myObj = Object.create(anotherObj);
for(var k in myObj) {
    console.log("found: " + k);
}
// found: a
("a" in myObj); // true
instanceof原理.png

instanceof是判断实例对象的_proto__和生成该实例的构造函数的prototype是不是引用的同一个地址,是返回true,否返回false。
注意:实例的instanceof在比较的时候,与原型链上的构造函数相比都是true。

var M = function (name) { this.name = name; }
var o3 = new M('o3')
var o5 = new M()
o3.__proto__.say = function(){
   console.log('hello world')
}

o3.say()
o5.say()

只有函数有prototype,对象是没有的。但是函数也是有__proto__的,因为函数也是对象。函数的__proto__指向的是Function.prototype。也就是说普通函数是Function这个构造函数的一个实例。

constructor

function Foo() { /*  .. */ }
Foo.prototype = { /* .. */ } // 创建一个新原型对象

var a1 = new Foo();
a1.constructor === Foo; // false!
a1.constructor === Object; // true!

Foo.prototype的constructor属性只是Foo函数在声明时的默认属性。如果创建一个新对象并替换函数默认的.prototype对象引用,那么新对象并不会自动获得.constructor属性。

属性设置和屏蔽

myObj.foo = 'bar';

如果myObj中包含为foo的普通数据访问属性,这条赋值语句只会修改已有的属性值。如果foo不是直接存在于myObj中,原型链就会被遍历,类似[[Get]]操作。如果原型链上找不到foo,foo就会被直接添加到myObj上。

如果foo既出现在myObj中也出现在原型链上层,则会发生屏蔽。myObj中的foo属性会屏蔽原型链上层的所有foo属性,因为myObj.foo总会选择原型链中最底层的foo属性。

如果foo不直接存在于myObj中而存在于原型链上层时,会出现下面三种情况:

  1. 如果原型链上层存在名为foo的普通数据访问属性并且没有被标记为只读(writable: false),那就会直接在myObj上添加foo属性,它是屏蔽属性。
  2. 如果原型链上层存在名为foo的普通数据访问属性并且被标记为只读(writable: false),那么无法修改已有属性或创建屏蔽属性。严格模式下会报错。
  3. 如果原型链上层存在foo并且它是一个setter,那会调用这个setter,foo不会被添加到myObj,也不会重新定义foo。(注:与实际代码不符)

例2

var o = Object.create(Object.prototype, {
  foo: { 
    writable:false,
    configurable:true,
    value: "hello" 
}});

var o2 = Object.create(o);

o2.foo = 123;  // 无效

console.log(o2); // {}

例3

var obj2 = {
       val:200
};
obj2.__defineGetter__('name',function(){return this.val});
obj2.__defineSetter__('name',function(name){this.val = name;});

var myObj = Object.create(obj2);
myObj.name = '999';
// {val: "999"}
    name: "999"
    val: "999"
    __proto__:
            name: "999"
            val: 200
            get name: ƒ ()
            set name: ƒ (name)
            __proto__: Object
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容