Giter VIP home page Giter VIP logo

Comments (136)

shaopower avatar shaopower commented on May 2, 2024 42

@littleluckly
你这样的话,增加 Child.prototype.testProp = 1; 同时会影响 Parent.prototype 的
如果不模拟,直接上 es5 的话应该是下面这样吧
Child.prototype = Object.create(Parent.prototype);

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 24

哈哈,这道题难住我了,对这个方面并没有研究,抱歉也提供不了什么资料,不过我会把这个课题记下来,如果想明白了或者有所进展,立刻跟题主讨论哈~

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 17

嗯嗯,确实任重而道远,暂时我也没有想明白,还需要学习很多东西,非常感谢你的分析~ o( ̄▽ ̄)d

from blog.

shaopower avatar shaopower commented on May 2, 2024 12

我觉得 js 脚本之前如果没有出现过 css 标签,那这部分脚本是不会被 css 文件阻塞的,就是说下面这块代码不会被阻塞

function printH2() {
    console.log('first script', document.querySelectorAll('h2'));
}
printH2()
setTimeout(printH2)

bootstrap.css 影响阻塞的是 second script,而且我觉得只有外链的 css 才会阻塞 js,内联的不会
在 chrome 的 timeline 或者 performance下可以看到

css 外链的时候

parse html;  
request bootstrap.css;
printH2();
setTimeout(printH2);
if (css 有缓存或者响应快){ 
    //receive css
    parse stylesheet;    
    second script;
    paint 页面;
    timeout fire;
} else {
    timeout fire;
    // receive css
    parse stylesheet;
    second script;
    paint 页面;
}

可以看到 second script 是在 parse stylesheet 之后的。

为什么有这个 if else 分支,我猜测浏览器做的某些处理,只是保证不会解析 css 后的 js 加入 event loop,也就不会执行了, 但是已经在里面的还是会执行完吧

css 改成内联后

走的逻辑类似 if css 缓存那个分支,而且没有 parse stylesheet 这一条了,parse html 也有差别,最开始就直接 parse 完了整个页面

具体为什么 css 会延迟 js 执行,我觉得这是浏览器的一种实现方式吧。

如果浏览器尚未完成 CSSOM 的下载和构建,而我们却想在此时运行脚本,会怎样?答案很简单,对性能不利:浏览器将延迟脚本执行和 DOM 构建,直至其完成 CSSOM 的下载和构建。

https://developers.google.com/web/fundamentals/performance/critical-rendering-path/adding-interactivity-with-javascript

另外并行的 js 请求也不是等全部都下载完才执行的哈,如果 script 不带 async 的话,按照在 html 里的上下位置来执行的,当然和网络也有关系。

例如下面的 2个 js 会同时请求
如果 1.js 5秒后返回,2.js 10秒后返回。在5秒的时候 1.js 会执行,10秒的时候 2.js 执行。
但是如果反过来 1.js 10秒后返回,2.js 5秒,那会在10秒的时候执行 1.js,1.js执行完开始再执行 2.js

<body>
<script src="1.js"></script>
<script src="2.js"></script>
</body>

setTimeout 是属于 macrotask,你的例子里应该和 microtask 关系不大, Promise then 和 MutationObserver 这些算 microtask

from blog.

Xchen1995 avatar Xchen1995 commented on May 2, 2024 10
function Person(name) {
    this.name = name
    this.setAge = function (age) {
        this.age = age
        return this
    }
    this.setPosition = function (pos) {
        this.position = pos
        return this
    }
    this.sayHi = function () {
        console.log(`Hello,my name is ${this.name},${this.age} years old, I am a ${this.position}`)
    }
}
var person = new Person('jim');
person.setAge(21).setPosition('developer').sayHi();

@littleluckly

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 9

@satorioh Object.create 的模拟实现如下:

Object.create = function( o ) {
    function f(){}
    f.prototype = o;
    return new f;
}

使用 new Parent会带来副作用,副作用如你所说,如果抛开这一点的话,我觉得两句代码效果应该是一样的。最终 Child.prototype 的原型都是指向 Parent.prototype。

关于第二个问题,constructor确实不会影响原型链继承,constructor 用来指向对象关联的函数,我在实际开发中倒没有用过 constructor,值得注意的一点就是如果用一个新对象覆盖函数的 prototype 属性值,新对象没有 constructor 属性,实例不能通过 constructor 指向正确的构造函数。

嗯,好像没有回答什么有用的哈~

from blog.

shaopower avatar shaopower commented on May 2, 2024 8

@jawil ,其实不一定是 setTimeout(printH2) 早于 second script 的
要看 bootstrap.css 这个文件获取的时机,如果 css 已经被浏览器缓存着的话(譬如第二次访问) second script 可能会先于 setTimeout。

照理说second script 应该是会被 css 阻塞执行的。不过之前都是看的 css 在 head 里的表现,放在 body 里是不是也有阻塞一说不太清楚。

只是我测下来的现象 disable cache 的话怎么刷新 second script 肯定在最后,但是把 disable cache 勾选去掉的话,后两个出现的顺序不固定。

浏览器里面的线程也希望有个大神能分享下,之前也遇到过很奇怪的表现,譬如 js 早于 css 或者不晚于 css 10ms 以内,浏览器会等 js 执行完再渲染;如果 晚于 css 10ms 之后网络再收到 js 响应,浏览器会先渲染再执行 js。

from blog.

 avatar commented on May 2, 2024 4

@jawil 关于游览器线程,掘金上之前有很多文章说明,我放出我的收藏吧
http://blog.csdn.net/alex8046/article/details/51914205
https://segmentfault.com/a/1190000012806637
https://juejin.im/post/59e85eebf265da430d571f89
https://juejin.im/post/5a6547d0f265da3e283a1df7
https://juejin.im/post/5a6155126fb9a01cb64edb45
https://juejin.im/post/5a69885351882573467d2a93

from blog.

luckymore avatar luckymore commented on May 2, 2024 4

so。。。

  • ”借用构造函数“解决属性共享、构造函数传参问题
    Parent.call(this, name);
  • ”Object.create“解决继承父级原型、多次调用父级构造函数的问题
    Object.create(Parent.prototype)
  • 还有个同学说,为啥不能 Child.prototype = Parent.prototype
    // 这两种方式,增加了一级原型链(__proto__),相当于隔离了Child和Parent
    Child.prototype = new Parent()
    // or
    Child.prototype = Object.create(Parent.prototype)

from blog.

STLighter avatar STLighter commented on May 2, 2024 3

@hjscript 在Parent.call(this, name);这句的时候执行Parent函数, 并将Child构造函数中的this作为执行时的this, 在执行Parent函数过程中给this赋值了一个colors数组, 即此时实例对象上有自己的colors.

这样子类实例会优先找到新赋值的colors数组, 而不会沿原型链找原型上的colors.

from blog.

yingye avatar yingye commented on May 2, 2024 3

最后一个寄生组合式继承,这样写起来可以更容易理解~

function Parent (name) {
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
}

Parent.prototype.getName = function () {
    console.log(this.name)
}

function Child (name, age) {
    Parent.call(this, name);
    this.age = age;
}

// 关键的三步
// var F = function () {};

// F.prototype = Parent.prototype;

// Child.prototype = new F();

var temp = Object.create(Parent.prototype)
temp.constructor = Child
Child.prototype = temp


var child1 = new Child('kevin', '18');

console.log(child1);

from blog.

fiveLucky avatar fiveLucky commented on May 2, 2024 3

@littleluckly
你这样的话,增加 Child.prototype.testProp = 1; 同时会影响 Parent.prototype 的
如果不模拟,直接上 es5 的话应该是下面这样吧
Child.prototype = Object.create(Parent.prototype);

其实也可以 Child.prototype.__proto__=Parent.prototype,使用寄生或者Object.create的目的就是形成原型链,不知道我说的对不😁

from blog.

zhanziyang avatar zhanziyang commented on May 2, 2024 2

感谢楼主的整理归纳,很清晰了!👍

有一个小遗漏想指出一下:("最后我们封装一下这个继承方法:"这一句上面的代码中)

Child.prototype = new F();

之后好像少了这个:

Child.prototype.constructor = Child;

from blog.

openfe-openfe avatar openfe-openfe commented on May 2, 2024 2
function Person (name) {
    this.name = name
}
Person.prototype = {
    setAge(age) {
        this.age = age
        return this
    },
    setPosition(pos) {
        this.position = pos
        return this
    },
    sayHi() {
        console.log(`Hello,my name is ${this.name},${this.age} years old, I am a ${this.position}`)
    }
}
let person = new Person('jim');
person.setAge(21).setPosition('developer').sayHi()



// class 方式

class Person {
    constructor(name) {
        this.name = name
    }
    setAge(age) {
        this.age = age
        return this
    }
    setPosition(pos) {
        this.position = pos
        return this
    }
    sayHi() {
        console.log(`Hello,my name is ${this.name},${this.age} years old, I am a ${this.position}`)
    }
}

let person = new Person('jim');
person.setAge(21).setPosition('developer').sayHi()

from blog.

satorioh avatar satorioh commented on May 2, 2024 1

@mqyqingfeng 好的,还是要感谢博主的耐心解答

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 1

@qujsh 关键在于直接访问的时候,会产生多余的属性值:

function Parent (name) {
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
}

Parent.prototype.getName = function () {
    console.log(this.name)
}

function Child (name, age) {
    Parent.call(this, name);
    this.age = age;
}

Child.prototype = new Parent();

var child1 = new Child('kevin', '18');

console.log(child1)

结果为:

default

你会发现有两个 ['red', 'blue', 'green'] 数组,实际上,Parent 中的那个是没有必要的

为了避免这种情况,才采用了间接调用

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 1

@cposture

第一个问题,需要了解一点原型链的知识,可以参考这篇文章 JavaScript深入之从原型到原型链,当 Child.prototype = new Parent() 的时候,如果 new Child 得到一个实例对象 child:

child.__proto__ === Child.prototype;
Child.prototype.__proto__ === Parent.prototype

这意味着如果访问 child 对象的属性时,如果在 child 对象中找不到,就会从 Child.prototype 这个对象找,如果还找不到就会从 Parent.prototype 中找,从而实现了 “继承” 的效果。

第二个问题是继承了方法共享的优点~

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 1

@suminhohu 第二种并不能算是继承,我们画个原型图:

使用 Object.create 的关系图为:

default

使用 extend 的关系图为:

default

使用 extend,我们仅仅是给 AlertModal.prototype 添加各种方法而已,就类似于 AlertModal.prototype.a = fn

如果非要说区别的话,比如我给 Modal.prototype 添加了一个函数, 使用 Object.create 实现继承的话,alertModal 可以自动获得这个方法,而使用 extend 的方式,必须要再执行一次 _.extend(AlertModal.prototype, Modal.prototype) 才能让 alertModal 获得该方法

from blog.

cklwblove avatar cklwblove commented on May 2, 2024 1

楼主,你好!发现了一个代码拼写错误,在4.原型式继承 -> person1.firends.push('taylor'); 中的friends拼写错误。

from blog.

xiaolan92 avatar xiaolan92 commented on May 2, 2024 1

@Hisons

function object(o) {
function F() {}
F.prototype = new o(); //这个地方写错了,改成F.prototype=o就行了
return new F();
}

from blog.

wengang-dev avatar wengang-dev commented on May 2, 2024 1

HTML5规范要求:
脚本执行前,在出现当前<script>之前的<link rel="stylesheet">必须完全载入。
脚本执行会阻塞DOM解析。

CSS外链阻塞了脚本的执行,此时异步队列的任务该如何调度呢?想我JS引擎主线程遇到阻塞后,这时候就会放弃当前线程的代码执行,时候这JS引擎的英文空闲的,为了避免等待白白浪费时间,所以主线程才会读取任务队列,开始执行异步任务。等CSS外链下载完成之后console.log('second script')难道成了下一个task

还有就是别人总结的这句话,一直不太懂,这种循坏机制到底是怎么工作的,描述的也比较抽象。

同时,异步任务又可以分为两种,macrotask(宏任务)和微(微任务)。在挂起任务时,JS引擎会将所有任务按照类别分到这两个队列中,首先在macrotask的队列(这个队列也被叫做任务队列)中取出第一个任务,执行完毕后取出微任务队列中的所有任务顺序执行;之后再取macrotask任务,周而复始,直至两个队列的任务都取完。

😨😨😨,浏览器渲染解析线程调度这一块任重而道远。

HTML5规范要求:
脚本执行前,在出现当前<script>之前的<link rel="stylesheet">必须完全载入。
脚本执行会阻塞DOM解析。

CSS外链阻塞了脚本的执行,此时异步队列的任务该如何调度呢?想我JS引擎主线程遇到阻塞后,这时候就会放弃当前线程的代码执行,时候这JS引擎的英文空闲的,为了避免等待白白浪费时间,所以主线程才会读取任务队列,开始执行异步任务。等CSS外链下载完成之后console.log('second script')难道成了下一个task

还有就是别人总结的这句话,一直不太懂,这种循坏机制到底是怎么工作的,描述的也比较抽象。

同时,异步任务又可以分为两种,macrotask(宏任务)和微(微任务)。在挂起任务时,JS引擎会将所有任务按照类别分到这两个队列中,首先在macrotask的队列(这个队列也被叫做任务队列)中取出第一个任务,执行完毕后取出微任务队列中的所有任务顺序执行;之后再取macrotask任务,周而复始,直至两个队列的任务都取完。

😨😨😨,浏览器渲染解析线程调度这一块任重而道远。

因为js中可能会有获取css样式的代码,所以css文件会阻塞js执行,js被阻塞时并不会说这么智能的因为空闲就会去执行异步任务,如果遇到异步任务,并不会阻塞JS,而是js继续向下执行,当异步任务执行条件满足时,将需要回调执行的函数加入任务队列,只有当js执行栈为空时才会读取任务队列中的任务(执行全部),将其压入执行栈,而且会优先执行微任务(比如promise),微任务执行后有必要的话会执行ui更新(没有微任务的话有必要的话也会执行UI更新),可以去 https://www.youtube.com/watch?time_continue=45&v=8aGhZQkoFbQ

from blog.

inottn avatar inottn commented on May 2, 2024 1

@zhidong10

prototype(Child, Parent) 这句是用来实现原型链的继承。你说的构造函数的继承是在 Parent.call(this, name) 这句实现的,两个合起来才是寄生组合式继承。

from blog.

STLighter avatar STLighter commented on May 2, 2024 1

@STLighter 朋友你好!其实你说的这个Parent.call(this).我是这么理解的,你看对不对啊?在Child实例后,Parent的colors会复制一份变成Child实例化之后的实例属性.多次new Child()之后都有各自的colors.所以通过操作 new Child().colors属性,其实操作的是new Child()自己的实例属性,不是原型属性。因为原型链的关系,会首先在实例对象上找,再到原型上去找。此外说到组合继承的重复问题。其实是重复了Child的原型属性colors. Patent.call(this)一次, new Parent()一次.

我语言表达能力不好,还是自学的.我这样理解对吗?@STLighter

并不是拷贝,而是创建了一个新的数组赋值,Parent.call(this)就是简单的执行Parent函数。 我们换成执行Parent.call(that)这样看起来比较清楚。在执行到this.colors = ['red', 'blue', 'green'];时其实此时的this就是我们传入的that,具体你可以查查callapply两个函数的作用。也就是说在执行Parent函数时,这个函数给我们传入的that赋值了一个新的colors数组([]语法是创建数组哦)。

sorry,很久没看了,回复有点晚。

from blog.

notzheng avatar notzheng commented on May 2, 2024 1

@chentianyuan

请问寄生组合继承中这一句怎么理解?

避免了在 Parent.prototype 上面创建不必要的、多余的属性

这一点应该是博主笔误了,应该是 “避免了在 Child.prototype 上面创建不必要的、多余的属性”。
我们使用Child.prototype = new Parent()时,会再次执行 Parent 的构造函数,这样 导致Child.prototype 中也会有一组构造函数中定义的属性,而这组属性是不必要的。
IMG_7CC080F180B6-1

from blog.

jawil avatar jawil commented on May 2, 2024

博主还没写事件循环和浏览器渲染机制这块,借宝地这里有个问题提前探讨一下。

<html>
<body>
    <h2>Hello</h2>
    <script>
    function printH2() {
        console.log('first script', document.querySelectorAll('h2'));
    }
    printH2()
    setTimeout(printH2)
    </script>
    <link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/4.0.0-alpha.4/css/bootstrap.css">
    <h2>World</h2>
    <script>
    console.log('second script');
    </script>
</body>
</html>

这个demo的效果是:控制台先打印出来了printH2(),setTimeout(printH2)的结果(说明此时的DOMtree已经完成),然后浏览器显示了页面,页面完全显示之后(RenderTree完成)才执行了 console.log('second script');

有个地方不明白,就是js的异步任务与UI线程之间的调度,这个demo的结果来看,DOM节点渲染之后生成DOMtree才开始执行 setTimeout(printH2)的printH2函数,所以打印出了两个h2节点。
博主能帮忙解答一下UI线程和异步任务之间的关系到底是怎么样的吗?或者对于这一块有什么好的文章学习吗

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@shaopower 竟然还有这么奇怪的表现,感觉开启了新的世界,看来要尽快的去探索一下~

from blog.

jawil avatar jawil commented on May 2, 2024
  • CSS(外链或内联)会阻塞整个DOM的渲染(Rendering),然而DOM解析(Parsing)会正常进行
  • 很多浏览器中,CSS会延迟脚本执行和DOMContentLoaded事件
  • JS(外链或内联)会阻塞后续DOM的解析(Parsing),后续DOM的渲染(Rendering)也将被阻塞
  • JS前的DOM可以正常解析(Parsing)和渲染(Rendering)

但是,我不明白的是,既然CSS会延迟脚本的执行,我把bootstrap那个换成内联样式,就是不发送http请求下载那个css样式,好像结果又变了,这时候构建CSStree好像并没有阻塞js脚本的执行,那么,到底是HTTP线程阻塞了脚本的执行,还是构建CSStree阻塞了脚本执行?

如果是HTTP线程阻塞了js的执行,这个也可以解释,为什么多个js并行下载,要等全部下载完成才执行一个道理,我继续尝试了一下,换成img标签,这个也会发送http请求,但是这个并不会阻塞脚本的执行。

如果是构建CSStree阻塞了线程,根据上面实践的结果,发现构建CSStree并没有阻塞脚本的运行。之前看到的下面的这个说法,也就不成立。

JS 的执行有可能依赖最新样式。 比如,可能会有 var width = $('#id').width(). 这意味着,JS 代码在执行前,浏览器必须保证在此 JS 之前的所有 css(无论外链还是内嵌)都已下载和解析完成。这是 CSS 阻塞后续 JS 执行的根本原因。

那么我的猜测就是,发送的请求是是样式文件会阻塞js脚本的执行,但为什么css会阻塞js脚本文件的执行,这个我暂时也不清楚,还有浏览器预加载一些机制也不太清楚。JS 的执行有可能依赖最新样式???难道我测的结果有问题?

from blog.

jawil avatar jawil commented on May 2, 2024

HTML5规范要求:
脚本执行前,出现在当前<script>之前的<link rel="stylesheet">必须完全载入。
脚本执行会阻塞DOM解析。

CSS外链阻塞了脚本的执行,此时异步队列的任务该如何调度呢?我想JS引擎主线程遇到阻塞后,这时候就会放弃当前线程的代码执行,这时候JS引擎是空闲的,为了避免等待白白浪费时间,所以主线程才会读取任务队列,开始执行异步任务。等CSS外链下载完成之后console.log('second script')难道成了下一个task

还有就是别人总结的这句话,一直不太懂,这种循坏机制到底是怎么工作的,描述的也比较抽象。

同时,异步任务又可以分为两种,macrotask(宏任务)和micro(微任务)。在挂起任务时,JS 引擎会将所有任务按照类别分到这两个队列中,首先在 macrotask 的队列(这个队列也被叫做 task queue)中取出第一个任务,执行完毕后取出 microtask 队列中的所有任务顺序执行;之后再取 macrotask 任务,周而复始,直至两个队列的任务都取完。

😨😨😨,浏览器渲染解析线程调度这一块任重而道远。

from blog.

littleluckly avatar littleluckly commented on May 2, 2024

写的很透彻,我得慢慢消化

from blog.

littleluckly avatar littleluckly commented on May 2, 2024

请教一个问题。为什么不让 Child.prototype 直接访问到 Parent.prototype ?一定要通过寄生的方式呢。。。
`
function Parent (name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}

Parent.prototype.getName = function () {
console.log(this.name)
}

function Child (name, age) {
Parent.call(this, name);
this.age = age;
}

// 关键的三步
//var F = function () {};

//F.prototype = Parent.prototype;

//Child.prototype = new F();
//我的修改
Child.prototype = Parent.prototype;

var child1 = new Child('kevin', '18');

console.log(child1);`

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@shaopower 十分感谢回答,正解,b( ̄▽ ̄)d

from blog.

littleluckly avatar littleluckly commented on May 2, 2024

@shaopower @mqyqingfeng 谢谢解惑!

from blog.

satorioh avatar satorioh commented on May 2, 2024

看了博主的文章,真是受益匪浅,新手想请教两个问题:

1.Child.prototype = Object.create(Parent.prototype);和 Child.prototype = new Parent();
这两句,除去后者会继承Parent构造函数中多余的属性/方法外,这两句代码是否是等效的?即Child.prototype最终都会指向Parent.prototype,都不破坏原型链?

2.原型上的constructor属性,除了能让子实例获得正确的父构造函数,感觉并不影响原型链继承?实际开发中是否还有其他用处?

感谢!

from blog.

lynn1824 avatar lynn1824 commented on May 2, 2024

今天一天一口气看完了博主所有的文章,分析的很透彻。希望能多写文章,多交流,共同学习进度,非常感谢楼主的分享...

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@lynn1824 与你共勉,[]~( ̄▽ ̄)~*

from blog.

522363215 avatar 522363215 commented on May 2, 2024

老铁,看完你文章,以前有好多问题都理清楚啦,看来还是要往底层深入,哈哈,谢谢博主!

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@522363215 恭喜你看完~ []~( ̄▽ ̄)~* JavaScript 还有很多的底层知识需要探索和总结~ 与你共勉~

from blog.

zeroone001 avatar zeroone001 commented on May 2, 2024

🙏楼主的分享~~~谢谢,对基础再次扎实了~~~

from blog.

littleluckly avatar littleluckly commented on May 2, 2024

今天朋友问了我一个问题,但是我也搞不懂,所以特地来请教博主,希望给予解答。请实现函数Person,该函数实现功能如下:

var person=new Person('jim');
person.setAge(21).setPosition('developer').sayHi();
//输出‘Hello,my name is jim,21 years old, I am a developer’

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@Xchen1995 感谢回答哈~ @littleluckly 正是如此~ 当然这些方法你也可以写在 Person.prototype 上 ~

from blog.

Xchen1995 avatar Xchen1995 commented on May 2, 2024

看了你 深入系列和 专题系列 受益良多 作者用心了

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@zhanziyang 感谢补充哈~ 确实需要这句~

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@songhaoreact 感谢补充~ 前者的写法也非常常见,而且通过 return this 实现连缀,后者则是 ES6 的写法~

from blog.

qujsh avatar qujsh commented on May 2, 2024

这句话 “如果我们不使用 Child.prototype = new Parent() ,而是间接的让 Child.prototype 访问到 Parent.prototype 呢?”

// 关键的三步
var F = function () {};

F.prototype = Parent.prototype;

Child.prototype = new F();

直接访问和间接访问,除了Parent的构造函数区分,还有什么不同吗?我是感觉绕了个圈

from blog.

allenGKC avatar allenGKC commented on May 2, 2024

楼主你好,文章写的很不错
有个小的问题,对于原型链继承中Child.prototype = new Parent();后面是否应该增加一句Child.prototype.constructor = Child,否则对于Child的实例对象child,它的constructor属性指向了Parent
这样逻辑理解是否有问题?

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@allenGKC 没有问题的,确实应该这样~ 只是这篇文章为了不增加大家的理解成本,就把这个忽略了~ 感谢补充哈~

from blog.

allenGKC avatar allenGKC commented on May 2, 2024

@mqyqingfeng 没事,我也是正好看到了,你的文章很不错,看完会有豁然开朗的感觉,感谢!

from blog.

cposture avatar cposture commented on May 2, 2024

请教一下,

  1. 组合继承中为什么需要 Child.prototype = new Parent(); 这样做有什么作用吗?
  2. 组合继承继承了原型链继承的什么优点呢?

from blog.

Sphinm avatar Sphinm commented on May 2, 2024

刚刚不小心误删了题目 =。=
不过真的很感谢帮我缕清头绪。通过Object.create方式会使 AlertModal.prototype.constructor === Modal
,指回去就好了~

from blog.

superyorklin avatar superyorklin commented on May 2, 2024

第n次阅读了,每次都有新感觉。
楼主,是不是可以直接Child.prototype.__proto__ = Parent.prototype,这样应该没有new Parent()的副作用,也不需要手动把Child.prototype.constructor指回去吧?

from blog.

lizhongzhen11 avatar lizhongzhen11 commented on May 2, 2024

打卡,看完了。
虽然很多看的依然不大懂,但是留个印象,随着开发经验积累再过来看看应该能理解更深了

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@superyorklin 主要是 __proto__ 是一个非标准的属性,(虽然已经入了规范的附录部分),在实际的开发中,还不会直接使用~

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@lizhongzhen11 欢迎随时回来看看哈~ 有问题也可以留言哈~

from blog.

rennaiqian avatar rennaiqian commented on May 2, 2024

寄生式继承那里的object.create是不是应该是Object.create

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@rennaiqian 是的,我写错了,感谢指出哈~

from blog.

273539918 avatar 273539918 commented on May 2, 2024

方法3的组合继承,应该加上这一句吧
Child.prototype.constructor = Child;

不然 Child实例的构造函数,会指向Parent

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@273539918 好嘞,加上~ 感谢 😀

from blog.

yunyu950908 avatar yunyu950908 commented on May 2, 2024

1.引用类型的属性被所有实例共享,举个例子:

function Parent () {
    this.names = ['kevin', 'daisy'];
}

function Child () {

}

Child.prototype = new Parent();

var child1 = new Child();

child1.names.push('yayu');

console.log(child1.names); // ["kevin", "daisy", "yayu"]

var child2 = new Child();

console.log(child2.names); // ["kevin", "daisy", "yayu"]

这个说明不太好吧,Child.prototype 指向 Parent 的实例,接着的 child1.names.push 在查找 names 的时候直接找了原型上的属性 child1.__proto__.names,而原型上的属性都来自于 Child.prototype,所以本来就是共享的。push 只是操作了这个引用。

如果直接重新赋值,给它创建一个私有的 names

...
child1.names = [];
console.log(child1.names); // []
console.log(child1.names === child1.__proto__.names); // false
...
console.log(child2.names); //  ["kevin", "daisy", "yayu"]
console.log(child2.names === child2.__proto__.names); // true

后面的这个例子:

function Parent () {
    this.names = ['kevin', 'daisy'];
}

function Child () {
    Parent.call(this); // 这里做了一个 this 传递 ==> Child ==> Parent
}

var child1 = new Child();
// 因为 Child 中做了 this 传递,所以这时候是私有的 child1.names, 而不是 child1.__proto__.names

// 所以这里只是修改实例对象 child1 的私有属性
child1.names.push('yayu');
console.log(child1.names); // ["kevin", "daisy", "yayu"]

var child2 = new Child();

// 而并不影响 child2 的私有属性
console.log(child2.names); // ["kevin", "daisy"] 

个人觉得上一条的结论 引用类型的属性被所有实例共享 这么写有点欠缺,这里或许用私有属性公有属性原型链查找来解释比较好吧。

PS:这个问题是别个萌新看了您的这篇博文被绕了,然后我第一眼也被这个论点给迷了(其实我也是个萌新⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄),然后才有这么个小建议 =。=

from blog.

hsroad avatar hsroad commented on May 2, 2024

你好 yayu 问下组合继承最后为嘛child1 和 child2 还是不能共享colors?不是已经原型共享了吗?

from blog.

STLighter avatar STLighter commented on May 2, 2024

__proto__在实际中是有应用的, 比如vue源代码中的对数组操作进行拦截https://github.com/vuejs/vue/blob/dev/src/core/observer/index.js#L86

这样写是因为组合寄生式继承在数组继承上存在一些问题, 例如:

// 组合寄生
function MyArray (...args) {
  Array.call(this, ...args);
}
var F = function () {};
F.prototype = Array.prototype;
MyArray.prototype = new F();

// 构造函数传入参数
const arr = new Array(1,2,3);
const myArr = new MyArray(1,2,3);

// 结果
console.log(arr); // (3) [1, 2, 3]
console.log(myArr); // MyArray {}

// 使用push使两者内容相同
myArr.push(1,2,3);
console.log(arr) // (3) [1, 2, 3]
console.log(myArr); // MyArray(3) [1, 2, 3]

// 修改length
arr.length = myArr.length = 1;
// 结果
console.log(arr) // [1]
console.log(myArr) // MyArray [1, 1: 2, 2: 3]

相关介绍: 为什么 es5 不能完美继承数组

from blog.

sfsoul avatar sfsoul commented on May 2, 2024

hello,在看你的很多博客之后,个人觉得自己从前也有很多没完全搞懂的地方。所以自己现在也想写blog记录下来。
博客这边博文就是基于当时看了你的继承这篇后自己的一些疑问写的。

from blog.

RThong avatar RThong commented on May 2, 2024

Child.prototype.__proto__ = Parent.prototype这样不行可以直接用Child.prototype = Object.create(Parent.prototype)啊,两者的意思是一样的

from blog.

colin1989 avatar colin1989 commented on May 2, 2024

设置 Child.prototype = Parent.prototype;
把代码封装起来,返回生成得对象.修改child2.proto , child1_proto_ 不会被修改。

prototype

但是在同一个函数里,会同步修改,有点不明白。


好像是原型链的问题,应该是在 ‘new’ 之后,不能原型链上进行赋值,否则会将之前的已生成的对象同步赋值

from blog.

xqchen233 avatar xqchen233 commented on May 2, 2024

看完了,纪念下,感谢

from blog.

Hisons avatar Hisons commented on May 2, 2024

楼主你好,我试的最后一个组合寄生继承,发现不能正确继承。改了之后可以继承,我改成
`function object(o) {
function F() {}
F.prototype = new o( ); //这行修改了
return new F();
}

function prototype(child, parent) {
var prototype = object(parent); //这行修改了
prototype.constructor = child;
child.prototype = prototype;
}

// 当我们使用的时候:
prototype(Child, Parent);
`
请你解释一下,为什么我把你代码抄了一遍,不能正确继承,我还不是特别明白。谢谢

from blog.

xiaolan92 avatar xiaolan92 commented on May 2, 2024

@STLighter 朋友你好!其实你说的这个Parent.call(this).我是这么理解的,你看对不对啊?在Child实例后,Parent的colors会复制一份变成Child实例化之后的实例属性.多次new Child()之后都有各自的colors.所以通过操作 new Child().colors属性,其实操作的是new Child()自己的实例属性,不是原型属性。因为原型链的关系,会首先在实例对象上找,再到原型上去找。此外说到组合继承的重复问题。其实是重复了Child的原型属性colors. Patent.call(this)一次, new Parent()一次.


我语言表达能力不好,还是自学的.我这样理解对吗?@STLighter

from blog.

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

@mqyqingfeng 你好博主,这是本人拜读此节文章 示例6后,及讨论中提出的问题的一个理解,不知正确否,还望能给予斧正,此外我正在做一个知识点的笔记,有引用到博主内容,都有标有引用出处,有些是直接复制的,如有不妥请告知,我将进行删除,谢谢

对于原文示例 6 寄生组合式继承,及直接写 Child.prototype = Parent.prototype; 的方式的理解

function Parent (name) {
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
}

Parent.prototype.getName = function () {
    console.log(this.name)
}

function Child (name, age) {
    Parent.call(this, name);
    this.age = age;
}

Child.prototype = new Parent();

var child1 = new Child('kevin', '18');
function Parent (name) {
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
}

Parent.prototype.getName = function () {
    console.log(this.name)
}

function Child (name, age) {
    Parent.call(this, name);
    this.age = age;
}

// 关键的三步
var F = function () {};

F.prototype = Parent.prototype;

Child.prototype = new F();


var child1 = new Child('kevin', '18');

这关键的第三步,个人这样理解的

第一种方式 Child.prototype = new Parent(),Child的原型直接指向的是Parent的 实例,这种方式会调用两次 Parent 构造这一点毋庸置疑,有意思的是修改为

// 关键的三步
var F = function () {};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype = Parent.prototype;

这两种方式,区别在于下边一种是将Child的原型直接指向了Parent的原型,因此在修改Child.prototype的时候,是会修改到Parent.prototype,因为这两个指向的是同一个对象(原型是一个对象),而使用 F 中间函数的方式,我的理解为 Child的原型指向F 的实例,而实例 new F() 的原型才是指向 Parent.prototype ,因此如下图:

Child.prototype -> `new F()`: F实例 -- F实例.__proto__ --> Parent.prototype -> {}:Parent的原型

那么在修改 Child.prototype 的时候,其实是在 实例F 上修改而已,没有直接在Parent.prototype 上修改

可以理解为在 Child 和 Parent 之间添加了一个 中间层 ,但是这并没有破坏原型的继承

from blog.

SilenceZeng avatar SilenceZeng commented on May 2, 2024

寄生组合式继承中对于完全支持ES5 的浏览器,object 函数可以直接换成 Object.create

from blog.

beyond5959 avatar beyond5959 commented on May 2, 2024
Child.prototype = Object.create(Parent.prototype);
Child.prototype.getColors = function() {};
const child = new Child('kevin', '18');

发现这样写之后 child 调不了getColors 方法呢,说方法不存在。

from blog.

EchoZhaoH avatar EchoZhaoH commented on May 2, 2024

@jawil
不知道我说的对不对,就我自己的理解
image

异步任务队列分为宏队列和微队列

宏异步队列

setTimeoutsetInterval 之类

微异步队列

Promise
当编译器解析代码时会按照这两类异步分别存入相应的异步队列
当主线程(先这样叫吧)中代码执行完毕,就会到异步队列中去取出之前存入异步任务并执行
我是把主线程理解为第一个宏任务,所以说只要是主线程代码执行完毕,就执行必然会去微任务队列异步任务
(个人理解)
node 环境测试也是如此
image

from blog.

qingyun1029 avatar qingyun1029 commented on May 2, 2024

@mqyqingfeng
你好,请教一下!
在第二种继承方式——“借用构造函数(经典继承)”里面提到“避免了引用类型的属性被所有实例共享”是什么意思?下面的例子中并没有看出什么不同!请讲一下自己的理解!谢谢!!

from blog.

zhidong10 avatar zhidong10 commented on May 2, 2024

prototype(Child, Parent);
Parent 内部的this.name this.colors并没有被继承啊
为什么这个就是理想的了呢

from blog.

inottn avatar inottn commented on May 2, 2024

@qingyun1029
我感觉例子表达的挺明显了。

  1. 原型链继承
var child1 = new Child();
child1.names.push('yayu');
console.log(child1.names); // ["kevin", "daisy", "yayu"]

var child2 = new Child();
console.log(child2.names); // ["kevin", "daisy", "yayu"]

console.log(child1.names === child2.names) // true

通过 console.log(child1.names === child2.names) // true ,可以看出如果属性是引用类型(例如对象),两个实例的属性指向的是同一个对象。

  1. 借用构造函数(经典继承)
var child1 = new Child();
child1.names.push('yayu');
console.log(child1.names); // ["kevin", "daisy", "yayu"]

var child2 = new Child();
console.log(child2.names); // ["kevin", "daisy"]

console.log(child1.names === child2.names) // false

通过 console.log(child1.names === child2.names) // ,可以看出两个实例的属性指向的是不同的对象。

from blog.

AnsonZnl avatar AnsonZnl commented on May 2, 2024

原型链继承那一节,friends写错了,写成了firends。

from blog.

a87604476 avatar a87604476 commented on May 2, 2024

个人认为高程对继承这块内容说的不是很好,js中只有对象,没有父子类因为继承意味着父类的函数和属性会复制给子类但js中只有引用不存在复制,更多的应该是使用委托,原型继承确实有共享引用变量的问题,但应该把行为封装在一个对象中,而对状态的维护保存在其他对象中。
比如:var o = {
gerFriends:function(){
return this.friends
},
appendFriends:function(name){
this.friends.push(name)
}
}
var o1 = Object.create(o);
o1.friends = ['tom','jerry','Michael'];
o1.appendFriends('james');
console.log(o1.gerFriends()); //["tom", "jerry", "Michael", "james"]
var o2 = Object.create(o);
console.log(o2.gerFriends()); //undefined
类似这种形式,把基础行为封装在一个对象中,把状态的维护封装在其他对象中,其他对象需要基础行为的时候可以委托给这个对象处理,也可以避免引用问题。

from blog.

SanQiG avatar SanQiG commented on May 2, 2024

有关事件循环机制可以看下这篇文章,我感觉讲的挺好的

from blog.

hzjswlgbsj avatar hzjswlgbsj commented on May 2, 2024

有个疑问希望有同学解答一下,以下这段代码来自MDN:

// 定义Person构造器
function Person(firstName) {
  this.firstName = firstName;
}

// 在Person.prototype中加入方法
Person.prototype.walk = function(){
  alert("I am walking!");
};
Person.prototype.sayHello = function(){
  alert("Hello, I'm " + this.firstName);
};

// 定义Student构造器
function Student(firstName, subject) {
  // 调用父类构造器, 确保(使用Function#call)"this" 在调用过程中设置正确
  Person.call(this, firstName);

  // 初始化Student类特有属性
  this.subject = subject;
};

// 建立一个由Person.prototype继承而来的Student.prototype对象.
// 注意: 常见的错误是使用 "new Person()"来建立Student.prototype.
// 这样做的错误之处有很多, 最重要的一点是我们在实例化时
// 不能赋予Person类任何的FirstName参数
// 调用Person的正确位置如下,我们从Student中来调用它
Student.prototype = Object.create(Person.prototype); // See note below

// 设置"constructor" 属性指向Student
Student.prototype.constructor = Student;

// 更换"sayHello" 方法
Student.prototype.sayHello = function(){
  console.log("Hello, I'm " + this.firstName + ". I'm studying " + this.subject + ".");
};

// 加入"sayGoodBye" 方法
Student.prototype.sayGoodBye = function(){
  console.log("Goodbye!");
};

// 测试实例:
var student1 = new Student("Janet", "Applied Physics");
student1.sayHello();   // "Hello, I'm Janet. I'm studying Applied Physics."
student1.walk();       // "I am walking!"
student1.sayGoodBye(); // "Goodbye!"

// Check that instanceof works correctly
console.log(student1 instanceof Person);  // true 
console.log(student1 instanceof Student); // true

上面的这段代码基本上就是组合式继承,在本文中以及《JavaScript高级程序设计》中,都是用Child.prototype = new Parent();来继承方法的。但是来自MDN的这段代码中却是使用Student.prototype = Object.create(Person.prototype),并且注释明确说直接new Parent()有错误,他说的错误我不理解。他说最重要的一点是我们在实例化时不能赋予Person类任何的FirstName参数,我们不是在子类的构造函数中赋予了Person类的参数吗?
正如文中提到的,Object.create的简单实现过程是这样的:

function createObj(o) {
    function F(){}
    F.prototype = o;
    return new F();
}

那么在这里也仅仅是返回的是一个prototype被赋值的实例而已,跟直接new Parent(),基本是一个意思吧。

from blog.

wuyupeng123 avatar wuyupeng123 commented on May 2, 2024

Child.prototype.constructor = Child,想问一下这行代码有什么用,Child.prototype.constructor不是本来就指向Child的构造函数的吗?求大佬指点

from blog.

zhw2590582 avatar zhw2590582 commented on May 2, 2024
Child.prototype = new Parent();

不鼓励以上写法了,既然只想继承,就不应该运行Parent构造函数而产生副作用,使用以下写法

Child.prototype = Object.create(Parent.prototype)

from blog.

yanghuiqi avatar yanghuiqi commented on May 2, 2024

Child.prototype = Object.create(Parent.prototype,{
constructor:{
value: Child,
enumerable: false,
writable: true,
configurable: true
}
})
这样,解决了 子类构造器问题,也解决了 子类会出现多余父类属性的问题。

from blog.

jqClub avatar jqClub commented on May 2, 2024

找到问题了:上面案例中的friends firends,单词写错了,前后不一致

第4个原型式继承,执行下面的报错了,可以帮忙看下么?@mqyqingfeng
Uncaught TypeError: Cannot read property 'push' of undefined
at :12:17

function createObj(o) {
function F(){}
F.prototype = o;
log(8888, new F())
return new F();
}

var person = {
name: 'kevin',
friends: ['daisy', 'kelly'],
}

var person1 = createObj(person);
// var person2 = createObj(person);

// person1.name = 'person1';
// console.log(person2.name); // kevin
log(11111, person1.firends)
person1.firends.push('taylor');
// console.log(person2.friends); // ["daisy", "kelly", "taylor"]

from blog.

cliYao avatar cliYao commented on May 2, 2024

看完博主的系列文章,收获颇丰,也更加认识到自己的不足,还需要再多多消化几遍,感谢🙏

from blog.

ShineKidd avatar ShineKidd commented on May 2, 2024

2.借用构造函数(经典继承)
Parent.prototype 上的方法,Child 也是无法调用的吧?这算是一个缺点么?

from blog.

pengpeng9413 avatar pengpeng9413 commented on May 2, 2024

function object(o) {
function F() {}
F.prototype = o;
return new F();
}

function prototype(child, parent) {
var prototype = object(parent.prototype);

prototype.constructor = child;
child.prototype = prototype;

}

// 当我们使用的时候:
prototype(Child, Parent);
请教一下,这两行代码怎么来解释它么,看的很迷糊这两行
prototype.constructor = child;
child.prototype = prototype;

from blog.

struggle-fish avatar struggle-fish commented on May 2, 2024

看完到大佬的文章总结了下,组合继承属性的那个
1、Child.prototype.__proto__ = Parent.prototype;
2、Object.setPrototypeOf(Child, Parent.prototype);
3、Child.prototype = Object.create(Parent.prototype, {
constructor: {
value: Child,
enumerable: false,
writable: true,
configurable: true
}
});
4、function object(o) {
function F() {}
F.prototype = o;
return new F();
}

function prototype(child, parent) {
var prototype = object(parent.prototype);
prototype.constructor = child;
child.prototype = prototype;
}

// 当我们使用的时候:
prototype(Child, Parent);

from blog.

zxl7 avatar zxl7 commented on May 2, 2024

以为是完结,一看评论这说的啥!!不是完结是新的开始,老铁们一起加油。

from blog.

coderew avatar coderew commented on May 2, 2024

在浏览器中运行:
Parent.prototype.getName = function() { console.log('Parent.prototype.getName==>', this.name); // kevin }

console.log('Child.prototype====>', child.getName()); // undefined

跟demo中结果不一样呢

from blog.

Daniel-Fang avatar Daniel-Fang commented on May 2, 2024

在浏览器中运行:
Parent.prototype.getName = function() { console.log('Parent.prototype.getName==>', this.name); // kevin }

console.log('Child.prototype====>', child.getName()); // undefined

跟demo中结果不一样呢

demo中没有retuen,所以是undefined

from blog.

luoping1998 avatar luoping1998 commented on May 2, 2024

今天朋友问了我一个问题,但是我也搞不懂,所以特地来请教博主,希望给予解答。请实现函数Person,该函数实现功能如下:

var person=new Person('jim');
person.setAge(21).setPosition('developer').sayHi();
//输出‘Hello,my name is jim,21 years old, I am a developer’
function Person(name) {
  this.name = name;
}

Person.prototype.setAge = function(age) {
  this.age = age;
  return this;
}

Person.prototype.setPosition = function(position) {
  this.position = position;
  return this;
}

Person.prototype.sayHi = function() {
  console.log(`Hello, my name is ${this.name}, ${this.age} years old, i am a ${this.position}`);
}

from blog.

lzxxxxx avatar lzxxxxx commented on May 2, 2024

看完到大佬的文章总结了下,组合继承属性的那个
1、Child.prototype.proto = Parent.prototype;
2、Object.setPrototypeOf(Child, Parent.prototype);
3、Child.prototype = Object.create(Parent.prototype, {
constructor: {
value: Child,
enumerable: false,
writable: true,
configurable: true
}
});
4、function object(o) {
function F() {}
F.prototype = o;
return new F();
}

function prototype(child, parent) {
var prototype = object(parent.prototype);
prototype.constructor = child;
child.prototype = prototype;
}

// 当我们使用的时候:
prototype(Child, Parent);

2、Object.setPrototypeOf(Child, Parent.prototype); 好像有点问题哎,是不是应该改成 Object.setPrototypeOf(Child.prototype, Parent.prototype);

from blog.

chentianyuan avatar chentianyuan commented on May 2, 2024

请问寄生组合继承中这一句怎么理解?

避免了在 Parent.prototype 上面创建不必要的、多余的属性

from blog.

A-cabbage avatar A-cabbage commented on May 2, 2024

有个疑问希望有同学解答一下,以下这段代码来自MDN:

// 定义Person构造器
function Person(firstName) {
  this.firstName = firstName;
}

// 在Person.prototype中加入方法
Person.prototype.walk = function(){
  alert("I am walking!");
};
Person.prototype.sayHello = function(){
  alert("Hello, I'm " + this.firstName);
};

// 定义Student构造器
function Student(firstName, subject) {
  // 调用父类构造器, 确保(使用Function#call)"this" 在调用过程中设置正确
  Person.call(this, firstName);

  // 初始化Student类特有属性
  this.subject = subject;
};

// 建立一个由Person.prototype继承而来的Student.prototype对象.
// 注意: 常见的错误是使用 "new Person()"来建立Student.prototype.
// 这样做的错误之处有很多, 最重要的一点是我们在实例化时
// 不能赋予Person类任何的FirstName参数
// 调用Person的正确位置如下,我们从Student中来调用它
Student.prototype = Object.create(Person.prototype); // See note below

// 设置"constructor" 属性指向Student
Student.prototype.constructor = Student;

// 更换"sayHello" 方法
Student.prototype.sayHello = function(){
  console.log("Hello, I'm " + this.firstName + ". I'm studying " + this.subject + ".");
};

// 加入"sayGoodBye" 方法
Student.prototype.sayGoodBye = function(){
  console.log("Goodbye!");
};

// 测试实例:
var student1 = new Student("Janet", "Applied Physics");
student1.sayHello();   // "Hello, I'm Janet. I'm studying Applied Physics."
student1.walk();       // "I am walking!"
student1.sayGoodBye(); // "Goodbye!"

// Check that instanceof works correctly
console.log(student1 instanceof Person);  // true 
console.log(student1 instanceof Student); // true

上面的这段代码基本上就是组合式继承,在本文中以及《JavaScript高级程序设计》中,都是用Child.prototype = new Parent();来继承方法的。但是来自MDN的这段代码中却是使用Student.prototype = Object.create(Person.prototype),并且注释明确说直接new Parent()有错误,他说的错误我不理解。他说最重要的一点是我们在实例化时不能赋予Person类任何的FirstName参数,我们不是在子类的构造函数中赋予了Person类的参数吗?
正如文中提到的,Object.create的简单实现过程是这样的:

function createObj(o) {
    function F(){}
    F.prototype = o;
    return new F();
}

那么在这里也仅仅是返回的是一个prototype被赋值的实例而已,跟直接new Parent(),基本是一个意思吧。

有个疑问希望有同学解答一下,以下这段代码来自MDN:

// 定义Person构造器
function Person(firstName) {
  this.firstName = firstName;
}

// 在Person.prototype中加入方法
Person.prototype.walk = function(){
  alert("I am walking!");
};
Person.prototype.sayHello = function(){
  alert("Hello, I'm " + this.firstName);
};

// 定义Student构造器
function Student(firstName, subject) {
  // 调用父类构造器, 确保(使用Function#call)"this" 在调用过程中设置正确
  Person.call(this, firstName);

  // 初始化Student类特有属性
  this.subject = subject;
};

// 建立一个由Person.prototype继承而来的Student.prototype对象.
// 注意: 常见的错误是使用 "new Person()"来建立Student.prototype.
// 这样做的错误之处有很多, 最重要的一点是我们在实例化时
// 不能赋予Person类任何的FirstName参数
// 调用Person的正确位置如下,我们从Student中来调用它
Student.prototype = Object.create(Person.prototype); // See note below

// 设置"constructor" 属性指向Student
Student.prototype.constructor = Student;

// 更换"sayHello" 方法
Student.prototype.sayHello = function(){
  console.log("Hello, I'm " + this.firstName + ". I'm studying " + this.subject + ".");
};

// 加入"sayGoodBye" 方法
Student.prototype.sayGoodBye = function(){
  console.log("Goodbye!");
};

// 测试实例:
var student1 = new Student("Janet", "Applied Physics");
student1.sayHello();   // "Hello, I'm Janet. I'm studying Applied Physics."
student1.walk();       // "I am walking!"
student1.sayGoodBye(); // "Goodbye!"

// Check that instanceof works correctly
console.log(student1 instanceof Person);  // true 
console.log(student1 instanceof Student); // true

上面的这段代码基本上就是组合式继承,在本文中以及《JavaScript高级程序设计》中,都是用Child.prototype = new Parent();来继承方法的。但是来自MDN的这段代码中却是使用Student.prototype = Object.create(Person.prototype),并且注释明确说直接new Parent()有错误,他说的错误我不理解。他说最重要的一点是我们在实例化时不能赋予Person类任何的FirstName参数,我们不是在子类的构造函数中赋予了Person类的参数吗?
正如文中提到的,Object.create的简单实现过程是这样的:

function createObj(o) {
    function F(){}
    F.prototype = o;
    return new F();
}

那么在这里也仅仅是返回的是一个prototype被赋值的实例而已,跟直接new Parent(),基本是一个意思吧。

Object.create跟直接new Parent()不一样的,博主在原型链继承中也提到了直接用new Parent()会导致引用类型的属性被实例共享,Object.create就如你所说的,实现过程是创建了一个新的构造函数,在这个例子中将这个构造函数的原型指向Person.prototype,然后从这个构造函数中返回出一个实例,这个实例就是新创建的这个对象,跟直接new Parent创建的实例是有区别的,希望能对你有所帮助,有什么不对的地方还请指出

from blog.

li305263 avatar li305263 commented on May 2, 2024

function Parent (name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}

Parent.prototype.getName = function () {
console.log(this.name)
}

function Child (name, age) {
Parent.call(this, name);
this.age = age;
}

//组合继承
Child .prototype = new Parent();

//寄生组合式继承
Child .prototype = Object.create(Person.prototype)

Child.prototype.constructor = Child;

也就是说组合继承 跟 寄生组合式继承 在于 Child .prototype 继承的写法上

from blog.

yuleiQ avatar yuleiQ commented on May 2, 2024

赞一个

from blog.

LoKiLyn avatar LoKiLyn commented on May 2, 2024

赞一个,发现一个小typo (firend)

from blog.

JiaJiaJiayi avatar JiaJiaJiayi commented on May 2, 2024

你好,想请教一下,最后寄生组合式继承可以直接用Child5.prototype.__proto__ = Parent5.prototype;省去新创建一个函数的开销吗?
完整代码如下:

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

function Child5(name, age) {
    this.age = age;
    Parent5.call(this, name);
}
Parent5.prototype.getInfo = function () { return this.name + ' ' + this.age }

Child5.prototype.__proto__ = Parent5.prototype;

from blog.

YunShengTeng avatar YunShengTeng commented on May 2, 2024

你好,想请教一下,最后寄生组合式继承可以直接用Child5.prototype.__proto__ = Parent5.prototype;省去新创建一个函数的开销吗?
完整代码如下:

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

function Child5(name, age) {
    this.age = age;
    Parent5.call(this, name);
}
Parent5.prototype.getInfo = function () { return this.name + ' ' + this.age }

Child5.prototype.__proto__ = Parent5.prototype;

如果原型上有引用类型,那么new出的实例被共享了,不安全

from blog.

jiangzhenxiang avatar jiangzhenxiang commented on May 2, 2024

请问 第六种寄生组合式继承,父类上的方法sayHello为什么不会被子类继承呢,执行下面代码会报错,求教

function Parent(name) {
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
}
Parent.sayHello = function () {
    console.log('hello');
}
Parent.prototype.getName = function () {
    console.log(this.name)
}

function Child(name, age) {
    Parent.call(this, name);
    this.age = age;
}
// 关键的三步
var F = function () {};

F.prototype = Parent.prototype;

Child.prototype = new F();

var child1 = new Child('kevin', '18');

console.log(child1);
child1.getName();
child1.sayHello();    //TypeError: child1.sayHello is not a function

from blog.

inottn avatar inottn commented on May 2, 2024

请问 第六种寄生组合式继承,父类上的方法sayHello为什么不会被子类继承呢,执行下面代码会报错,求教

function Parent(name) {
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
}
Parent.sayHello = function () {
    console.log('hello');
}
Parent.prototype.getName = function () {
    console.log(this.name)
}

function Child(name, age) {
    Parent.call(this, name);
    this.age = age;
}
// 关键的三步
var F = function () {};

F.prototype = Parent.prototype;

Child.prototype = new F();

var child1 = new Child('kevin', '18');

console.log(child1);
child1.getName();
child1.sayHello();    //TypeError: child1.sayHello is not a function

@jiangzhenxiang 首先明确几个概念
父类:Parent,子类:Child, 子类的实例:child
那么你的问题其实是 父类上的静态方法 sayHello 为什么不会被子类的实例继承,这个是符合预期的,实例是不会继承类的静态方法,因为JS的继承说白了就是顺着实例的__proto__属性访问类的prototype属性,所以只会继承类的prototype上的属性。

from blog.

jiangzhenxiang avatar jiangzhenxiang commented on May 2, 2024

@inottn 多谢指点

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.