创建对象

创建对象的几种方式

第一种:字面量

第二种:通过构造函数

第三种:Object.create

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>CreatObject</title>
</head>
<body>

</body>
<script>
    //第一种方式
    var o1={name:"o1"};
    var o2=new Object({name:"o2"});
   //第二种方式
    var A=function (name) {
        this.name=name;
    };
    var o3=new A("o3");
    //第三种方式
    var c={name:"c"};
    var o4=Object.create(c);
    console.log("o1",o1);
    console.log("o2",o2);
    console.log("A",A);
    console.log("o3",o3);
    console.log("o4",o4);
</script>
</html>

可以看出前2种方式创建的对象的结构是一样的(使用构造函数创建的对象他的constructor属性指向的是构造函数),第3种Object.create创建的对象他的结构和其他的不同,他先创建了一个{},再把这个对象的__proto__指向传入的参数。来看一些细节例子

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

</body>
<script>

//例一 var obj1 = {name:'one'}; obj2 = Object.create(obj1); obj2.name = 'two'; console.log(obj1.name);//one //例二 var obj3 = {prop:{name:'one'}}; obj4 = Object.create(obj3); obj4.prop.name = 'two'; console.log(obj3.prop.name);//two //例三 var obj5 = {list:['one','one','one']}; obj6 = Object.create(obj5); obj6.list[0] = 'two'; console.log(obj5.list[0]);//two </script> </html>

这个输出结果让人感到困惑,为什么结果后2个会输出two,要理解这个首先我们先来看一些js的规则:

摘抄自《JavaScript权威指南第6版》125-126页

1.假设要查询对象o的属性x,如果o中不存在x,那么将继续在o的原型对象中查询属性x。如果原型对象中也没有x,但这个原型对象也有原型,那么继续在这个原型对象的原型上执行查询,直到找到x或者查找到一个原型是null的对象为止。可以看到,对象的原型属性

构成了一个“链”,通过这个“链”可以实现属性的继承。

2.现在假设给对象o的属性x赋值,如果o中已经有属性x(这个属性不是继承来的),那么这个赋值操作只改变这个已有属性x的值如果o中不存在属性x,那么赋值操作给o添加一个新属性x。如果之前o继承自属性x,那么这个继承的属性就被新创建的同名属性覆盖

3.属性赋值操作首先先检查原型链,以此来判定是否允许辅助操作。例如,如果o继承自一个只读属性x,那么赋值操作是不允许的。如果允许属性赋值操作,它也总是在原始对象上创建属性或对已有的属性赋值,而不会去修改原型链。在JavaScript中,只有在查询属性

时才会体会到继承的存在,而设置属性则和继承无关,这是JavaScript的一个重要特性,该特性让程序可以有选择地覆盖继承的属性。

4.属性赋值要么失败,要么创建一个属性,要么在原始对象中设置属性,但有一个例外,如果o继承自属性下,而这个属性是一个具有setter方法的accessor属性,那么这时将调用setter方法而不是给o创建一个属性x。需要注意的是,setter方法是由对象o调用的,二不是定义这个属性的原型对象调用的。因此如果setter方法定义任意属性,这个操作只是针对o本身,并不会去修改原型链。

例一

我们先打印出obj2来看一下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
</body>
<script>
    var obj1 = {name:'one'};
    obj2 = Object.create(obj1);
    obj2.name = 'two';
    console.log(obj2);
</script>
</html>

 

可以看出在执行obj2.name = 'two'后obj2中有2个name属性,一个是在外层的name,一个是原型链上继承来的name(__proto__下的name属性)。为什么会这样呢?根据规则2,在执行这个赋值操作时,发现obj2中不存在属性name(不去原型链上找)后,不会再去原型链上找,而是直接在obj2上添加一个name属性!

如果不加这句话我们来打印一下obj2obj2.name

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
</body>
<script>
    var obj1 = {name:'one'};
    obj2 = Object.create(obj1);
    //obj2.name = 'two';
    console.log(obj2);
    console.log(obj2.name);
</script>
</html>

 

可以看出当我们只查询属性时,是按照上面的规则1执行查询的,先查询obj2本身,他没有name属性,然后去其原型链中查找,找到了name属性,最后返回。

例二

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
</body>
<script>
    var obj3 = {prop:{name:'one'}};
    obj4 = Object.create(obj3);
    //obj4.prop.name="two";
    console.log(obj3,obj4);
</script>
</html>

这里我们把obj4.prop.name="two"注释了,打印obj3obj4,没什么问题

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
</body>
<script>
    var obj3 = {prop:{name:'one'}};
    obj4 = Object.create(obj3);
    obj4.prop.name="two";
    console.log(obj3,obj4);
</script>
</html>

但当我们执行obj4.prop.name="two";发现obj3.prop.name的值也改变了,这是为什么呢?这与obj4.prop.name="two"这个语句的执行过程有关,语句执行时先进行第一个点运算即找prop属性,再进行第二个点运算找name,最后进行赋值操作。进行第一个点运算完全属于属性的查找遵循规则1,即会在原型链中找到prop属性。第二个点运算和赋值操作遵循规则2,prop有name属性,直接赋值。因为最终找到的prop.name是在原型链中找到的,所以赋值修改了原型链!!所以obj3改变了!我们可以修改代码如下再看一下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
</body>
<script>
    var obj3 = {prop:{name:'one'}};
    obj4 = Object.create(obj3);
    obj4.prop="two";
    console.log(obj3,obj4);
</script>
</html>

我们直接执行obj4.prop="two",出现了例二中我们讨论的情况。

例三

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
</body>
<script>
    var obj5 = {list:['one','one','one']};
    obj6 = Object.create(obj5);
    obj6.list[0] = 'two';
    console.log(obj5,obj6);
</script>
</html>

例三与例二中的情况一样,obj6.list[0] = 'two'进行了2次查找和赋值,先查list,再查list[0],最后赋值。第一步查找遵循规则1,后面2步遵循规则2。

总结

在没有访问器属性时,对象的属性一次查找和赋值不会改变其原型链,如果是进行了多次查找再赋值则可能改变。

转载于:https://www.cnblogs.com/dengcun/p/8862297.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值