Giter VIP home page Giter VIP logo

Comments (65)

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 28

@qujsh 原型也是一个对象,我们假设这个对象叫做 O,看这个例子:

var a = {
     b: O
}

你看 a.b 指向了 O 对象,就相当于 Person.prototype 指向了原型对象这句话。

再看这个例子:

function Person(name) {
    this.name = name;
    if (typeof this.getName != "function") {
        Person.prototype = {
            constructor: Person,
            getName: function () {
                console.log(this.name);
            }
        }
    }
}

var person1 = new Person('kevin');

当 new Person() 的时候,是先建立的原型关系,即 person .__proto__ = Person.prototype,而后修改了 Person.prototype 的值,这就相当于:

// O 表示原型对象
var O = {};

var a = {
     b: O
}

先建立原型关系,指的是 c.__proto__ = a.b = O

而后修改 Person.prototype 的值,相当于

var anotherO = {};
a.b = anotherO;

即便修改了 Person.prototype 的值,但是 c.__proto__ 还是指向以前的 O

不知道这样解释的清不清楚,欢迎交流~

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 13

@ry928330

function Person(name) {
    this.name = name;
    if (typeof this.getName != "function") {
        Person.prototype = {
            constructor: Person,
            getName: function () {
                console.log(this.name);
            }
        }
    }
}

var person1 = new Person('kevin');

以这个例子为例,当执行 var person1 = new Person('kevin') 的时候,person1.的原型并不是指向 Person.prototype,而是指向 Person.prototype 指向的原型对象,我们假设这个原型对象名字为 O, 然后再修改 Person.prototype 的值为一个字面量,只是将一个新的值赋值给 Person.prototype, 并没有修改 O 对象,也不会切断已经建立的 person1 和 O 的原型关系,访问 person.getName 方法,依然会从 O 上查找

from blog.

liuxinqiong avatar liuxinqiong commented on May 2, 2024 10

感觉特么都有缺点啊

from blog.

alex1504 avatar alex1504 commented on May 2, 2024 3

@qujsh

和你一样,一开始也觉得这句话是在难以理解。

“当执行 var person1 = new Person('kevin') 的时候,person1.的原型并不是指向 Person.prototype,而是指向 Person.prototype 指向的原型对象“

后来想想,其实重点在js创建一个对象时是先建立原型关系,而后执行构造函数才是这个问题的核心。

“当 new Person() 的时候,是先建立的原型关系,即 person .proto = Person.prototype,而后修改了 Person.prototype 的值”

画图理解一下就很清晰:
bb.png

初始先存在constructor关系线和1关系线。

var person1 = new Person('kevin');

由于new Person()先建立原型关系导致了关系2的生成,然后覆盖了构造函数指向了原型之后,关系1移除,关系3生成。

var person2 = new Person('daisy');

在person1生成后由于关系3替代了关系1,所以在person2生成时同时生成了关系4(即person2的__proto__指向了新的原型对象),所以person2能找到新的原型对象上的方法。

当person1和person2生成后的最终关系图
nn.png

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 2

@monkeySmoke 共享的写在 prototype 中,独立的写在构造函数中。

我们以弹窗组件举个例子:

function Dialog(options) {
   this.options = options;
}

Dialog.prototype.show = function(){...}

如果我们一个页面用到多个 dialog:

var dialog1 = new Dialog({value: 1});
var dialog2  = new Dialog({value: 2});

dialog1 和 dialog2 传入的参数不一样,写在构造函数中,我们可以通过

console.log(dialog1.options) 访问 dialog1 的配置选项

而对于 show 方法而言,所以的 dialog 都是公用的,所以写在 prototype 属性中

from blog.

Xing-He avatar Xing-He commented on May 2, 2024 2

@mqyqingfeng 不知道对于这个 示例 4.1 的理解是否正确,还望能指出

function Person(name) {
    this.name = name;
    if (typeof this.getName != "function") {
        Person.prototype = {
            constructor: Person,
            getName: function () {
                console.log(this.name);
            }
        }
    }
}

var person1 = new Person('kevin');
var person2 = new Person('daisy');

// 报错 并没有该方法
person1.getName();

// 注释掉上面的代码,这句是可以执行的。
person2.getName();

个人对于原文这个示例的理解:

讨论中有个示例图,解释的比较清楚的一点是:js创建一个对象时是 先建立原型关系,而 后执行构造函数
那么在 第一个 var person1= new Person('Kevin') 调用的时候,函数(类)的 Person.prototype 还并没有被修改,然后再执行类似 Person.apply(obj) 的操作,在这个apply操作中,构造被执行,那么 if 里边的内容被执行,然后 Person.prototype 才被修改,指向新的一个字面量对象,
重点是,这个时候 person1 的原型还是指向的被 修改之前Person.prototype,而在第二次 var person2 = new Person('Daisy') 的时候,Person.prototype 已经被修改,因此 person1 原型上是没有 getName,而 person2 可以正常调用

from blog.

WHITE-ILMARE avatar WHITE-ILMARE commented on May 2, 2024 2

您好,在4.1中不能使用字面量这个问题时。如果new内部实现是先执行apply再修改新建对象原型的指向,应该就不会出现这种问题了是吗?

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 1

@MagicHacker 这是肯定的呀,因为当你第一次 new 的时候,person_1 的原型指向了 Person.prototype 所指向的那个对象,后来再修改 Person.prototype,只会使得以后 new 的对象指向这个新的 Person.prototype,已经 new 的不会再发生改变~

from blog.

WHITE-ILMARE avatar WHITE-ILMARE commented on May 2, 2024 1

@Xing-He 如果new内部实现是先执行apply的操作,这个时候就会修改Person构造函数的原型指向为那个字面量对象,再给新建的object的原型赋值,那不就双双指向那个字面量对象了吗?

from blog.

uniquexiaobai avatar uniquexiaobai commented on May 2, 2024 1

@yangjunfocus 每个实例都创建一份构造函数中的属性,=== 比较的是引用类型的地址,所以肯定 [] !== [] 呀

from blog.

TanYanjieZYX avatar TanYanjieZYX commented on May 2, 2024 1

看的深有领悟,特别是各位大大地评论

from blog.

xumengzi avatar xumengzi commented on May 2, 2024

消灭零回复!

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@JarvenIV 哈哈,(๑•̀ㅂ•́)و✧

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@liuxinqiong 确实如此哈,一般都是用组合模式

from blog.

ry928330 avatar ry928330 commented on May 2, 2024

@mqyqingfeng 你好,想请问你两个问题:一个是:4.1动态原型的模式,“使用字面量方式直接覆盖 Person.prototype,并不会更改实例的原型的值”这是为什么?另一个是5.1 寄生构造函数模式,在调用的时候和工厂模式相比,就多了一个new调用,这个有什么用?

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@ry928330 关于寄生构造函数模式的使用,例子中也有讲到,你可以理解为这个人就是想用 new 的方式而不是直接调用函数 😂

from blog.

ry928330 avatar ry928330 commented on May 2, 2024

@mqyqingfeng 那现在怎么通过Person再找回原来的prototype(也就是O)呢,因为像这样
function Person(name) {
this.name = name;
}
var person1 = new Person('kevin');
person1.proto === Person.prototype //true
现在给Person.prototype重新赋值为一个字面量后,person1.proto === Person.prototype 肯定是false,那还能从Person这个构造函数(对象),找到原来的prototype么(也就是O)?

from blog.

ry928330 avatar ry928330 commented on May 2, 2024

@mqyqingfeng 那现在怎么通过Person再找回原来的prototype(也就是O)呢,因为像这样
function Person(name) {
this.name = name;
}
var person1 = new Person('kevin');
person1.proto === Person.prototype //true
现在给Person.prototype重新赋值为一个字面量后,person1.proto === Person.prototype 肯定是false,那还能从Person这个构造函数(对象),找到原来的prototype么(也就是O)?

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@ry928330 不能啦~

from blog.

qujsh avatar qujsh commented on May 2, 2024
function Person(name) {
    this.name = name;
    if (typeof this.getName != "function") {
        Person.prototype = {
            constructor: Person,
            getName: function () {
                console.log(this.name);
            }
        }
    }
}

var person1 = new Person('kevin');

当执行 var person1 = new Person('kevin') 的时候,person1.的原型并不是指向 Person.prototype,而是指向 Person.prototype 指向的原型对象

对这句话其实不是很理解,为什么指向了Person.prototype指向的原型对象?
还有上文的一句话:
回顾下 apply 的实现步骤,会执行 obj.Person 方法,这个时候就会执行 if 语句里的内容,注意构造函数的 prototype 属性指向了实例的原型。
我脑子里的印象还是,person1.__proto__ = Person.prototype;

from blog.

yan919 avatar yan919 commented on May 2, 2024

请问组合模式哪些方法和属性写在prototype,哪些写在构造函数里

from blog.

MagicHacker avatar MagicHacker commented on May 2, 2024

第二个原型模式优化的内容,如果把新建对象放在用对象字面量重写原型对象之前,再调用原型上的方法就会报错。

function Person(){}

var person_1 = new Person();

Person.prototype = {
   constructor:Person,
   name:'Joe',
   getName : function(){
      console.log(this.name);
   }
}
person_1.getName(); //error

from blog.

geekzhanglei avatar geekzhanglei commented on May 2, 2024

谢谢作者。关于重写原型的问题,还是要好好看js高程p156-“原型的动态性”一节的,其中P157图6-3对更好理解字面量重写原型导致的原型链变化有帮助,结合图和new操作符的第二步与重写原型的先后关系就理解了这个问题。

from blog.

Tan90Qian avatar Tan90Qian commented on May 2, 2024

在《你不知道的JavaScript(上卷)》中,提到了一种被作者称为“面向委托”的设计思路,即:

Foo = {
  init: function(who) {
  this.me = who; },
  identify: function() {
    return "I am " + this.me;
  } 
};
Bar = Object.create( Foo );
Bar.speak = function() {
  alert( "Hello, " + this.identify() + "." );
};
var b1 = Object.create( Bar );
b1.init( "b1" );
var b2 = Object.create( Bar );
b2.init( "b2" );
b1.speak();
b2.speak();

作者认为在js中不存在“类”且“原型链”是引用而不是复制的情况下,强行使用“类”的设计思路会导致一些的问题,然后使用了这种“纯对象”的形式。
从作者给的思维模型图上看,似乎这种设计模式更加简洁易懂,为何不管是日常开发还是面试中,似乎都没见过这种风格的?它有什么致命缺陷么?还是要调整整个团队的习惯得不偿失?

from blog.

jgchenu avatar jgchenu commented on May 2, 2024

从深入第一篇看到这里即将完结,必须给大大点个赞!狂赞一下!写得太好看了,我在坐地铁还有睡觉前,手机微信网页都是置顶大大的博客来看的❤️❤️❤️

from blog.

easyhaloo avatar easyhaloo commented on May 2, 2024

博主我可以不可以这样理解person1.proto = Person.prototype 这句话就相当于person1.proto 指向了Person.prototype 指向的一块区域,当Person.prototype 以字面量的形式更改的时候,原来的那块区域并没有发生变化。感觉之前看:
person1.的原型并不是指向 Person.prototype,而是指向 Person.prototype 指向的原型对象
这句话都有点迷糊,这样理解是不是要好点

from blog.

Xing-He avatar Xing-He commented on May 2, 2024

@WHITE-ILMARE 个人觉得和 apply Person.prototype 的先后顺序应该没有关系,Person.prototype 是指向原型,如果修改为字面量的方式,是将原型指向地址修改为指向 字面量对象,那么在第一个 new Person() 的时候原型还是指向的 Person.prototype ,在第一个new 构造执行阶段,经过 if 判断后,原型被指向了 字面量对象,这样才造成了第一个 person1 没有函数,而第二个person2 可以调用到 getName 函数,因为 getName 是在字面量对象上定义的

from blog.

nabei avatar nabei commented on May 2, 2024

ES6 中 CLASS 本质是哪种写法?

from blog.

zhixinpeng avatar zhixinpeng commented on May 2, 2024

书上的那个原型图我真的看不懂

from blog.

ChenLeoXX avatar ChenLeoXX commented on May 2, 2024

@qujsh
其实就是复杂类型按引用传递的问题

var obj = {name:'l'}
var c= obj 
obj = {name:"b"}
console.log(c) //{name:'l'}
console.log(obj) // {name:'b'}

from blog.

yangzai316 avatar yangzai316 commented on May 2, 2024

1、直接字面量:var a = { };
2、new方法:var b = new Object();
上面两个算不算创建对象的方法?和他们有什么区别吗?
感觉这篇文章介绍的方法都是为了继承做准备...

from blog.

Latube avatar Latube commented on May 2, 2024

@xusongfu 不一样的哈,这里必须定义,代码如下:

function Person(name) {
    this.name = name;
}

Person.prototype = {
    constructor: Person,
    getName: function () {
        console.log(this.name);
    }
};

var person1 = new Person();

console.log((person1.constructor.toString()));   //打印出构造函数

function Person(name) {
    this.name = name;
}

Person.prototype = {
//     constructor: Person,
    getName: function () {
        console.log(this.name);
    }
};

var person1 = new Person();

console.log((person1.constructor.toString()));   //function Object() { [native code] }

from blog.

Latube avatar Latube commented on May 2, 2024

@yangzaiwangzi js高程上P84提到了两种方式生成的对象是一样的,但字面量形式不会调用Object构造函数

from blog.

Latube avatar Latube commented on May 2, 2024

@Xing-He 个人觉得还是有区别的,博主在上面也提到了new的过程依次如下:

  1. 首先新建一个对象
  2. 然后将对象的原型指向 Person.prototype
  3. 然后 Person.apply(obj)
  4. 返回这个对象

还是贴上代码:

function objectFactory() {

    var obj = new Object(),

    Constructor = [].shift.call(arguments);

    obj.__proto__ = Constructor.prototype;

    var ret = Constructor.apply(obj, arguments);

    return typeof ret === 'object' ? ret||obj : obj;//若构造函数返回null,则返回实例
z
};

function Person(name) {
    this.name = name;
    if (typeof this.getName != "function") {
        Person.prototype = {
            constructor: Person,
            getName: function () {
                console.log(this.name);
            }
        }
    }
}

var person1 = objectFactory(Person, 'kevin');

console.log(person1.getName());  //Uncaught TypeError: person1.getName is not a function

//调换顺序后

function objectFactory() {

    var obj = new Object(),

    Constructor = [].shift.call(arguments);

	var ret = Constructor.apply(obj, arguments);

    obj.__proto__ = Constructor.prototype;

    

    return typeof ret === 'object' ? ret||obj : obj;//若构造函数返回null,则返回实例
z
};

function Person(name) {
    this.name = name;
    if (typeof this.getName != "function") {
        Person.prototype = {
            constructor: Person,
            getName: function () {
                console.log(this.name);
            }
        }
		return new Person(name)
    }
}

var person1 = objectFactory(Person, 'kevin');

console.log(person1.getName());  //kevin

from blog.

Emensionyu avatar Emensionyu commented on May 2, 2024

感谢冴羽大大分享,对原型的理解和new的理解更深了一点

from blog.

yangjunfocus avatar yangjunfocus commented on May 2, 2024

感谢大神的分享!有个问题想要请教一下大神:
image
如图,同样是在构造函数里定义的属性并初始化,为什么person1.same==person2.same 和person1.friends==person2.friends 结果不一样?难道是 这两个属性的类型不一样导致的?一个基本类型值,一个引用类型值。还请大神不吝赐教!谢谢!

from blog.

SanQiG avatar SanQiG commented on May 2, 2024

有关4.1动态原型模式的问题可能和大大的思路不一样刚开始看也是有点懵,后来回过头去看了高程上156-157页有关动态原型的内容和那张图就明晰了。
不知道我这样理解对不对。。。我觉得根本原因就是重写了原型对象,实例与原型之间是松散连接关系,调用构造函数时会为实例添加一个指向最初原型的[[prototype]]指针,而把原型修改为另外一个对象就等于 切断了 构造函数与最初原型之间的联系。 new的底层实现是把实例对象的 proto 指向构造函数原型这一步放在了apply之前,所以在重写原型之前,实例对象与原型之前已经建立了连接。
高程157页图6-3告诉我们,重写原型对象切断了现有原型与任何之前已经存在的对象实例之间的联系,它们引用的仍然是最初的原型,这也就导致了为什么大大给出的例子中person1没有getName方法而person2却有。

from blog.

linxh0908 avatar linxh0908 commented on May 2, 2024

请问 第一个例子工厂模式,缺点是对象无法识别,我是这样理解,因为返回的是Object创建的对象,所以实例对象不能通过constructor找到对应的构造函数,但是你说的是因为所有的实例都指向同一个原型对象,能详细说下嘛

from blog.

Xing-He avatar Xing-He commented on May 2, 2024

@WHITE-ILMARE 好像你说的是正确的 ,如 @Latube 的代码,谢谢大佬

  • 先修改原型
function objectFactory() {
        var obj = new Object(),
        Constructor = [].shift.call(arguments);

        console.log('1 obj.__proto__',obj.__proto__)
        obj.__proto__ = Constructor.prototype;
	console.log('2 obj.__proto__',obj.__proto__)
	console.log('3 Constructor.prototype',Constructor.prototype)
	var ret = Constructor.apply(obj, arguments);
	console.log('4 obj.__proto__',obj.__proto__)
	console.log('5 Constructor.prototype',Constructor.prototype)
    
        return typeof ret === 'object' ? ret||obj : obj;
};

function Person(name) {
    this.name = name;
    if (typeof this.getName != "function") {
        Person.prototype = {
            constructor: Person,
            getName: function () {
                console.log(this.name);
            }
        }
    }
}

var person1 = objectFactory(Person, 'kevin');

------------------------------------------

输出:

1 obj.__proto__ {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
2 obj.__proto__ {constructor: ƒ}
3 Constructor.prototype {constructor: ƒ}
4 obj.__proto__ {constructor: ƒ}
5 Constructor.prototype {constructor: ƒ, getName: ƒ}


person1.getName
undefined
  • 先apply
function objectFactory() {
        var obj = new Object(),
        Constructor = [].shift.call(arguments);
	
	console.log('1 obj.__proto__',obj.__proto__)
	console.log('2 Constructor.prototype',Constructor.prototype)
	var ret = Constructor.apply(obj, arguments);
	console.log('3 obj.__proto__',obj.__proto__)
	console.log('4 Constructor.prototype',Constructor.prototype)
        obj.__proto__ = Constructor.prototype;
	console.log('5 obj.__proto__',obj.__proto__)
	console.log('6 Constructor.prototype',Constructor.prototype)
    
    return typeof ret === 'object' ? ret||obj : obj;
};

function Person(name) {
    this.name = name;
    if (typeof this.getName != "function") {
        Person.prototype = {
            constructor: Person,
            getName: function () {
                console.log(this.name);
            }
        }
    }
}

var person1 = objectFactory(Person, 'kevin');

------------------------------------------

输出:

1 obj.__proto__ {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
2 Constructor.prototype {constructor: ƒ}
3 obj.__proto__ {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
4 Constructor.prototype {constructor: ƒ, getName: ƒ}
5 obj.__proto__ {constructor: ƒ, getName: ƒ}
6 Constructor.prototype {constructor: ƒ, getName: ƒ}

person1.getName
ƒ () {  console.log(this.name);  }

from blog.

hfutpath avatar hfutpath commented on May 2, 2024

感谢感谢

from blog.

yanghuiqi avatar yanghuiqi commented on May 2, 2024

评论有趣

from blog.

onloner2012 avatar onloner2012 commented on May 2, 2024

感觉特么都有缺点啊

选取在你的项目中发挥最大效力的模式就行了呗

from blog.

itsuki0927 avatar itsuki0927 commented on May 2, 2024

动态原型模式为什么不能用字面量直接赋值,以下是我的理解:
举一个简单的例子:

let obj = {
    name: "obj"
};
let per = obj;
obj = {
    age: 20
};
// obj { age:20 }
// per { name:"obj" }

引用《你不知道的JS》中的一句话,更好的进行理解:

JavaScript中引用指向的是值。
简单值(null、undefined、字符串、数字、布尔和 ES6 中的 symbol)是通过值的复制。
而对象和函数则是通过引用的复制。

所以在上述例子中:
perobj都指向了值 { name:'obj' },然后又将obj重新赋值,由于引用指向的是值本身,也就是值{ name:'obj' },而不是perobj变量,所以obj的重新赋值无法更改per引用的指向,所以结果如最后所示。

回到这个例子当中:
假设 Person.prototype就是上述例子的obj,new出来的实例对象是per

  1. new 操作符会在内部绑定其对应的原型对象就是类似于上述例子中per = obj
  2. 然后在函数中给Person.prototype进行重新赋值,类似于上述例子中的obj = { age:10 }
  3. 此时实例对象per的指向的原型对象还是没有变的,它还是原来的那个{ name:"obj" },在上面没有getName这个方法,所以就会出现getName is not defined的异常。

from blog.

qiqingfu avatar qiqingfu commented on May 2, 2024

@mqyqingfeng @mqyqingfeng 你好,想请问你两个问题:一个是:4.1动态原型的模式,“使用字面量方式直接覆盖 Person.prototype,并不会更改实例的原型的值”这是为什么?另一个是5.1 寄生构造函数模式,在调用的时候和工厂模式相比,就多了一个new调用,这个有什么用?

4.1动态原型的模式

let obj = {
        a: 1
    }

  let prosen = obj
  obj = {
    a: 2
  }
      console.log(prosen) // {a: 1}
      console.log(obj) // {a: 2}

在堆内存中又开辟了一片空间, 切断了和之前的引用关系吗?

from blog.

superwtt avatar superwtt commented on May 2, 2024

@qujsh

和你一样,一开始也觉得这句话是在难以理解。

“当执行 var person1 = new Person('kevin') 的时候,person1.的原型并不是指向 Person.prototype,而是指向 Person.prototype 指向的原型对象“

后来想想,其实重点在js创建一个对象时是先建立原型关系,而后执行构造函数才是这个问题的核心。

“当 new Person() 的时候,是先建立的原型关系,即 person .proto = Person.prototype,而后修改了 Person.prototype 的值”

画图理解一下就很清晰:
bb.png

初始先存在constructor关系线和1关系线。

var person1 = new Person('kevin');

由于new Person()先建立原型关系导致了关系2的生成,然后覆盖了构造函数指向了原型之后,关系1移除,关系3生成。

var person2 = new Person('daisy');

在person1生成后由于关系3替代了关系1,所以在person2生成时同时生成了关系4(即person2的__proto__指向了新的原型对象),所以person2能找到新的原型对象上的方法。

当person1和person2生成后的最终关系图
nn.png

谢谢大佬的图,一下子理解了,但还是想问一下:
第一次new Person()生成关系网络的时候,this指代的是实例person1, 这个时候进入判断语句this.getName相当于person1.getName,为什么打印是undefined,而下面person1.getName()的时候直接报错?
我理解的是,程序在进入if语句的时候,实例找不到getName属性就应该直接报错,而不是变成undefined
求问大神怎么理解。

from blog.

luohong123 avatar luohong123 commented on May 2, 2024

《JavaScript高级程序设计》得反反复复看啊,一看就会,一写就忘 😄

from blog.

OldDream avatar OldDream commented on May 2, 2024

”这是为什么

第一个问题,我认为用类似C的指针来理解比较容易,虽然js里面似乎没有指针的概念。
构造函数中的prototype指向内存地址A,而Person.prototype = {} 赋值后指向地址B,两者其实不会相互影响。

from blog.

zhtzjz avatar zhtzjz commented on May 2, 2024

看到作者这边文章,拿起了我尘封多年的javascript高级程序设计

from blog.

xsfxtsxxr avatar xsfxtsxxr commented on May 2, 2024

寄生构造函数模式和工厂模式有啥区别,就是一个用new,一个不用new?

from blog.

puck1006 avatar puck1006 commented on May 2, 2024

@qujsh
和你一样,一开始也觉得这句话是在难以理解。
“当执行 var person1 = new Person('kevin') 的时候,person1.的原型并不是指向 Person.prototype,而是指向 Person.prototype 指向的原型对象“
后来想想,其实重点在js创建一个对象时是先建立原型关系,而后执行构造函数才是这个问题的核心。
“当 new Person() 的时候,是先建立的原型关系,即 person .proto = Person.prototype,而后修改了 Person.prototype 的值”
画图理解一下就很清晰:
bb.png
初始先存在constructor关系线和1关系线。

var person1 = new Person('kevin');

由于new Person()先建立原型关系导致了关系2的生成,然后覆盖了构造函数指向了原型之后,关系1移除,关系3生成。

var person2 = new Person('daisy');

在person1生成后由于关系3替代了关系1,所以在person2生成时同时生成了关系4(即person2的__proto__指向了新的原型对象),所以person2能找到新的原型对象上的方法。
当person1和person2生成后的最终关系图
nn.png

谢谢大佬的图,一下子理解了,但还是想问一下:
第一次new Person()生成关系网络的时候,this指代的是实例person1, 这个时候进入判断语句this.getName相当于person1.getName,为什么打印是undefined,而下面person1.getName()的时候直接报错?
我理解的是,程序在进入if语句的时候,实例找不到getName属性就应该直接报错,而不是变成undefined
求问大神怎么理解。

Javascript是动态类型语言。点就定义了,只是没赋值,所以是undefined。undefined不是一个函数,所以调用报错

from blog.

liyanana avatar liyanana commented on May 2, 2024

每次看都有新收获

from blog.

cw84973570 avatar cw84973570 commented on May 2, 2024
function Person(name) {
    this.name = name || 'keivn';
}

// 私有属性放到实例里
// Person.prototype.name = 'keivn';
Person.prototype.getName = function () {
    console.log(this.name);
};

var person1 = new Person();

请问原型模式这样写有什么问题吗?跟其他的原型模式相比有什么优缺点?还是说这个name属性是作为公有属性的?

from blog.

anjina avatar anjina commented on May 2, 2024
function Person(name) {
    this.name = name || 'keivn';
}

// 私有属性放到实例里
// Person.prototype.name = 'keivn';
Person.prototype.getName = function () {
    console.log(this.name);
};

var person1 = new Person();

请问原型模式这样写有什么问题吗?跟其他的原型模式相比有什么优缺点?还是说这个name属性是作为公有属性的?

你这是组合模式把, 构造函数 里面的 name 是每个实例对象的属性, 而原型上定义的才是实例对象共享的

from blog.

cw84973570 avatar cw84973570 commented on May 2, 2024
function Person(name) {
    this.name = name || 'keivn';
}

// 私有属性放到实例里
// Person.prototype.name = 'keivn';
Person.prototype.getName = function () {
    console.log(this.name);
};

var person1 = new Person();

请问原型模式这样写有什么问题吗?跟其他的原型模式相比有什么优缺点?还是说这个name属性是作为公有属性的?

你这是组合模式把, 构造函数 里面的 name 是每个实例对象的属性, 而原型上定义的才是实例对象共享的

@anjina 为什么文章里的组合模式要修改整个原型呢?在我的理解里最后实现的效果好像没区别,还是说两个写法都行?


又看了一遍,发现文章中说修改整个原型封装性会好点。。。。。

from blog.

cw84973570 avatar cw84973570 commented on May 2, 2024

请问 第一个例子工厂模式,缺点是对象无法识别,我是这样理解,因为返回的是Object创建的对象,所以实例对象不能通过constructor找到对应的构造函数,但是你说的是因为所有的实例都指向同一个原型对象,能详细说下嘛

大概是如果var o = new Object()不做修改的话,不管是createPeople还是createPerson所创建的实例都是指向同一个原型,即对象o的原型永远指向Object.prototype.

from blog.

haohongyang1 avatar haohongyang1 commented on May 2, 2024

这个文章是在讲js继承吗

from blog.

fatFire avatar fatFire commented on May 2, 2024
function person(name){
    var o = new Object();
    o.sayName = function(){
        console.log(name);
    };
    return o;
}

var person1 = person('kevin');

person1.sayName(); // kevin

person1.name = "daisy";

person1.sayName(); // kevin

console.log(person1.name); // daisy

稳妥稳妥构造函数模式 是不是sayName 这个函数在创建时作用域里保存了person函数的AO 所以每次调用都是kevin

from blog.

ConanLF avatar ConanLF commented on May 2, 2024

关于4.1的Person.prototype复写的理解:
//设地址是0x1234
var o = {
value: 1
}
//a.b指向0x1234
var a = {
b:o
}
var c = {}
//c.d指向a.b指向的地址 即0x1234
c.d = a.b
//设地址0x4321
var x = {
value: 2
}
//a.b指向新地址0x4321
a.b = x
//c.d地址是0x1234, a.b地址是0x4321
console.log(c.d === a.b) // false

把a.b换成Person.prototype, c.d换成person1.proto, o就是原来的原型实例, x就是复写的字面量
这样理解对不对

from blog.

xiaqingping avatar xiaqingping commented on May 2, 2024

稳妥构造函数模式和工厂模式我咋就看着一样呢,区别在哪

from blog.

wangxiaotian avatar wangxiaotian commented on May 2, 2024

@mqyqingfeng
关于4.1看楼主解释和评论区好像明白了,但还有个困惑

`function Person(name) {
this.name = name;
}

Person.prototype = {
constructor: Person,
getName: function () {
console.log(this.name);
}
};
var person1 = new Person();`

4.0例子里边添加原型的时候也是重写的,为啥这个的实例原型和原构造函数原型没有断掉呢?
没看4.1的时候,看4.0是山;看了4.1,感觉4.0不是山了!

from blog.

axiaoha avatar axiaoha commented on May 2, 2024

@qujsh 原型也是一个对象,我们假设这个对象叫做 O,看这个例子:

var a = {
     b: O
}

你看 a.b 指向了 O 对象,就相当于 Person.prototype 指向了原型对象这句话。

再看这个例子:

function Person(name) {
    this.name = name;
    if (typeof this.getName != "function") {
        Person.prototype = {
            constructor: Person,
            getName: function () {
                console.log(this.name);
            }
        }
    }
}

var person1 = new Person('kevin');

当 new Person() 的时候,是先建立的原型关系,即 person .proto = Person.prototype,而后修改了 Person.prototype 的值,这就相当于:

// O 表示原型对象
var O = {};

var a = {
     b: O
}

先建立原型关系,指的是 c.proto = a.b = O

而后修改 Person.prototype 的值,相当于

var anotherO = {};
a.b = anotherO;

即便修改了 Person.prototype 的值,但是 c.proto 还是指向以前的 O

不知道这样解释的清不清楚,欢迎交流~

image

function Person(name) {
   this.name = name
}

Person.prototype.getName = function () {
   console.log(this.name);
};
var person1 = new Person('kevin');
Person.prototype = {
   getName : 1
}
console.log(person1.__proto__);
console.log(person1.getName);
var person2 = new Person('kevin');
console.log(person2.__proto__);
console.log(person2.getName);

from blog.

liynxy avatar liynxy commented on May 2, 2024

@mqyqingfeng 不知道对于这个 示例 4.1 的理解是否正确,还望能指出

function Person(name) {
    this.name = name;
    if (typeof this.getName != "function") {
        Person.prototype = {
            constructor: Person,
            getName: function () {
                console.log(this.name);
            }
        }
    }
}

var person1 = new Person('kevin');
var person2 = new Person('daisy');

// 报错 并没有该方法
person1.getName();

// 注释掉上面的代码,这句是可以执行的。
person2.getName();

个人对于原文这个示例的理解:

讨论中有个示例图,解释的比较清楚的一点是:js创建一个对象时是 先建立原型关系,而 后执行构造函数
那么在 第一个 var person1= new Person('Kevin') 调用的时候,函数(类)的 Person.prototype 还并没有被修改,然后再执行类似 Person.apply(obj) 的操作,在这个apply操作中,构造被执行,那么 if 里边的内容被执行,然后 Person.prototype 才被修改,指向新的一个字面量对象,
重点是,这个时候 person1 的原型还是指向的被 修改之前Person.prototype,而在第二次 var person2 = new Person('Daisy') 的时候,Person.prototype 已经被修改,因此 person1 原型上是没有 getName,而 person2 可以正常调用

我的理解是,实例是在原型重写之前还是之后创建的问题。
demo
创建 person1 实例的时候,关联的是最初的原型,然后原型就被重写了。

但在创建 person2 的时候,关联的就是重写之后的原型了。

而后面通过 return new Person(name) 这种方式来解决,其实就是在重写原型之后,再重新创建实例,这时候的实例关联的就是重写之后的原型。

from blog.

Jamartin-create avatar Jamartin-create commented on May 2, 2024

image
这个应该改成Array.push.apply(values, arguments); 吧

from blog.

DaphnisLi avatar DaphnisLi commented on May 2, 2024

怎么感觉在讲继承似的

from blog.

tinyblckc0000al avatar tinyblckc0000al commented on May 2, 2024

@mqyqingfeng 关于4.1看楼主解释和评论区好像明白了,但还有个困惑

`function Person(name) { this.name = name; }

Person.prototype = { constructor: Person, getName: function () { console.log(this.name); } }; var person1 = new Person();`

4.0例子里边添加原型的时候也是重写的,为啥这个的实例原型和原构造函数原型没有断掉呢? 没看4.1的时候,看4.0是山;看了4.1,感觉4.0不是山了!

4.0添加原型是在构造函数外添加的,只会执行一次,可以看成定义而不是重写,所有构造出来的对象实例引用的prototype都是同一个。
而4.1是每次构造函数都会执行,所以旧的对象所引用的prototype会因为新的对象构造过程而被覆盖。

from blog.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.