zoukankan      html  css  js  c++  java
  • 关于Object.create()与原型链的面试题?

    原文地址 https://segmentfault.com/q/1010000004670616?utm_source=weekly&utm_medium=email&utm_campaign=email_weekly 

    本处是转载学习 

    javascript  zhuzi 6 天前提问 · 6 天前更新
    关注  15 关注
    收藏  3 收藏,284 浏览
    问题对人有帮助,内容完整,我也想知道答案1 问题没有实际价值,缺少关键内容,没有改进余地
    一:

    var obj1 = {name:'one'};
    obj2 = Object.create(obj1);
    obj2.name = 'two';
    console.log(obj1.name);
    //one
    二:

    var obj1 = {prop:{name:'one'}};
    obj2 = Object.create(obj1);
    obj2.prop.name = 'two';
    console.log(obj1.prop.name);
    //two
    三:

    var obj1 = {list:['one','one','one']};
    obj2 = Object.create(obj1);
    obj2.list[0] = 'two';
    console.log(obj1.list[0]);
    //two
    为什么后面两段代码修改的是原型链上的属性呢?

    问题是,为什么二、三中的代码不是像代码一中直接给obj2添加属性,而是修改了原型链上的属性?
    求解释下一、二、三的结果?
    6 天前提问 评论
    默认排序时间排序
    8 个回答

    答案对人有帮助,有参考价值1 答案没帮助,是错误的答案,答非所问
    问题出在赋值name时上了

    只解释下一和二,二和三同理,就不多说了





    我们可以看出,Object.create(obj1)都是把obj2的__proto__指向了obj1

    但是下面的那么赋值是不一样的

    一在赋值前可以输出下obj2.name的值,其实是one,赋值的时候obj2.name,会当成属性去找,但是这时候是把自身的name属性赋值成了two,并不会动obj1中的属性,也就是说对象的属性是无法修改其原型链中的同名属性,而只会自身创建一个同名的属性并为其赋值。

    而二在赋值的时候,prop.name是当成一个对象去处理,发现自己没有prop对象,就会去原型链里去找,发现在obj1中找到了name,所以变成了'two'

    如果一想达到同样的效果,可以使用obj2.__proto__.name去修改obj1中的name值

    自己的理解,如有不对,请指出学习~
    6 天前回答 · 6 天前更新  3 评论

    gismanli
    49 声望
    答案对人有帮助,有参考价值1 答案没帮助,是错误的答案,答非所问
    个人是觉得楼主对一个Object的get和set操作没有理解透彻。

    get操作就是查询作用域中/对象中是否存在某个变量/属性,如果存在,则返回其对应的值。对于写代码的人来说,就是获取某个变量/属性的值,即Get。而Set则与之相反,是往作用域中/对象中的某个变量/属性里存值,即设置值(Set)。

    第一个例子
    obj2.name = 'two'这句话完成了一个get和一个set。
    首先是从当前作用域中get到obj2,发现是存在的,于是得到它的值 {__proto__: {name: 'one'}},这里我把它的和这个题相关的原型链写进去了。浏览器里这个的颜色是偏暗的。
    接着便是往obj2中的name属性上set了一个'two'这样的字符串值。

    至于这个值为啥没有设置到obj1上的name上,你肯定很好奇,且听我慢慢道来。
    往obj2的name属性上set值,在内部会转换成 obj2.[[Set]]('name', 'two', obj2)
    你多半看不懂这个O.[[Set]](P, V, R)是什么鬼,它会走如下流程:

    让 ownDesc = Object.getOwnPropertyDescriptor(O, P);
    如果 ownDesc === undefined,则进入下面步骤:
    让 parent = Object.getPrototypeOf(O);
    如果 parent !== null 则 执行 parent.[[Set]](P, V, R),并返回其结果;
    否则让 ownDesc 等于一个值为空,且可枚举,可配置,可修改的数据描述符。然后进入3
    如果 ownDesc 是数据描述符,则进入下面步骤:
    如果 ownDesc 不可配置,则返回false;
    如果 R 的类型不是Object,则返回false;
    让 existingDesc = Object.getOwnPropertyDescriptor(R, P);
    如果 existingDesc !== undefined 则进入如下步骤:
    如果existingDesc是访问器描述符,则直接返回false;
    如果existingDesc是不可修改的数据描述符,则返回false;
    在 R 上定义 P 属性,且值为 V,并返回该值。
    否则在 R 上创建一个新的 P 属性,且值为 V,并返回该值
    (进入到这里,说明ownDesc是个访问器描述符),让 setter = ownDesc.set;
    如果 setter === undefined, 返回false;
    返回 setter.call(R, V);
    这里面的你可能不了解的就是什么是数据描述符,什么是访问器描述符。其实区别他们的很简单,就是看有没有get或set函数,如果有get和set之一或都有,则是访问器描述符,否则就是数据描述符,数据描述符一定有value,value的值可以为undefined;

    说到这里,第一个例子走的路线就是

    1 -> 2 -> 2.a -> 2.b -> 1 -> 3 -> 3.c -> 3.e
    于是就在 obj2本身上创建了一个name属性,二并没有修改到obj1的name属性。

    第二个例子
    obj2.prop.name = 'two';
    这是先在作用域中找 obj2,发现是存在的,于是得到它的值 {__proto__: {prop: {name: 'one'}}}
    然后再找 obj2 的 prop 属性。这里就是内部的Get操作了,obj2.prop 会在内部转换成
    obj2.[[Get]]('prop', obj2),你又看不懂这个O.[[Get]](P, R)了吧?它其实走的是下面的流程。

    让 desc = Object.getOwnPropertyDescriptor(O, P);
    如果 desc === undefined,则进入下面流程
    让 parent = Object.getPrototypeOf(O);
    如果 parent === null,返回 undefiend;
    返回 parent.[[Get]](P, R)的结果;
    如果 desc 是数据描述符,则返会 desc.value;
    此时,desc一定是访问器描述符,则让 getter = desc.get;
    如果 getter === undefined, 返回 undefined;
    返回 getter.call(R);
    于是在获取 obj2.prop时,走的路线如下

    1 -> 2 -> 2.a -> 2.c -> 1 -> 3
    得到 obj2.prop 的值为 obj1.prop 为 {name: 'one'},
    即 obj2.prop === obj1.prop === {name: 'one'};
    由于 obj2.prop和obj1.prop 都指向了 {name: 'one'},
    所以你再修改obj2.prop的name属性时,便修改了obj1.prop的name属性。

    第三个例子,这个和第二个例子类似,获取 obj2.list 的时候也是返回的 obj1.list 的引用,所以都能修改。
    6 天前回答 · 6 天前更新  3 评论

    solar
    2.4k 声望
    答案对人有帮助,有参考价值0 答案没帮助,是错误的答案,答非所问
    obj2 = Object.create(obj1)是把obj1作为obj2的原型,所以obj2访问的是原型链的属性
    6 天前回答  评论

    LL89757
    291 声望
    答案对人有帮助,有参考价值0 答案没帮助,是错误的答案,答非所问
    http://www.cnblogs.com/shuiyi/p/5305435.html 刚看到的博客 写的不错
    6 天前回答  评论

    猴年生猴子
    17 声望
    答案对人有帮助,有参考价值0 答案没帮助,是错误的答案,答非所问
    昨天翻来覆去睡不着,现在才明白原来答错一道题,罪过罪过。。

    原因是没有弄清楚Object()的作用,把它当构造函数了。。看别人的答案吧,讲得挺细的。

    原答案:
    什么原型链啊,这根本就是考察基本类型和引用类型的传值方式。。
    原型只存在于函数中,原型链才可以存在于所有引用类型中!
    你可以打印一下obj1.prototype(原型),obj1.__proto__(原型链)。

    关于传值,看下面的例子:

    a = 1;
    b = a;
    b = 2;
    console.log(a);//1

    a = {val: 1};
    b = a;
    b.val = 2;
    console.log(a.val);//2

    a = {val: 1};
    b = a;
    b = {val :2};      //注意这里,重写了b,这时b和a没半毛钱关系了!
    console.log(a.val);//1
    只能帮你到这了,自己悟去吧。。
    6 天前回答 · 6 天前更新  3 评论

    hiYoHoo
    1.9k 声望
    +1
    obj2对象中的prop属性已经在obj2内存中开辟了一块空间,只不过引用了obj1的属性值。换句话说,obj2已经具备了的prop这个属性,它并不需要从原型链上找,即使找,也不会去找obj1对象本身的属性。

    hiYoHoo · 6 天前

    展开评论
    答案对人有帮助,有参考价值0 答案没帮助,是错误的答案,答非所问
    自己找不到,就会去原型链上面找。
    第一个例子,obj1有name这个属性,value是1,所以就是1.
    第二个例子,obj1有一个属性叫prop,obj2没有这个属性,obj1是obj2的原型,是object.create()构造器赋予的,所以obj2的prop就找到了原型的prop,但是prop是个对象,所以这里存储的实际上是一个引用,也就是说,obj2也是找到了这个引用。相当于obj2._proto_.prop,改掉了引用的prop这个对象的name,obj1的引用没变,但是prop的name变了。
    第三个例子和第二个也一样。
    6 天前回答 · 6 天前更新  评论

    zhangway19921221
    3 声望
    答案对人有帮助,有参考价值0 答案没帮助,是错误的答案,答非所问
    Object.create方法指定的第1个参数为新建对象的原型对象
    在获取一个对象的属性值时,才会有可能沿着原型链向下寻找,属性赋值没有这个
    给一个对象属性赋值时,如果这个属性不存在,那么就直接为这个对象添加这个属性并赋值(不会去理会原型链中的存在,除了某些特殊情况,如原型链中有这个属性的set方法,或这个属性被设置只读不可写的)
    obj2.prop.name='two' 先计算obj2.prop的值,在原型链中被发现,然后再计算obj2.prop对应的对象(不检查原型链)中是否存在name属性~~~
    obj2.list[0] = 'two';
    也就是先计算obj2.list属性的值,然后赋值给obj2.list属性下标为0(属性名为“0”)的属性
    那么结果就好理解了吧
    6 天前回答  评论

  • 相关阅读:
    进度条
    html5 表单新增事件
    html5 表单的新增type属性
    html5 表单的新增元素
    html5 语义化标签
    jq 手风琴案例
    codeforces 702D D. Road to Post Office(数学)
    codeforces 702C C. Cellular Network(水题)
    codeforces 702B B. Powers of Two(水题)
    codeforces 702A A. Maximum Increase(水题)
  • 原文地址:https://www.cnblogs.com/shidengyun/p/5335760.html
Copyright © 2011-2022 走看看