国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

面試官問:JS的繼承

stonezhu / 2861人閱讀

摘要:用過的讀者知道,經(jīng)常用繼承。部分源碼使用點擊這里查看源碼面試官可以順著這個問繼承的相關問題,比如的繼承用如何實現(xiàn)。主要就是三點子類構造函數(shù)的指向父類構造器,繼承父類的靜態(tài)方法子類構造函數(shù)的的指向父類構造器的,繼承父類的方法。

用過React的讀者知道,經(jīng)常用extends繼承React.Component

// 部分源碼
function Component(props, context, updater) {
  // ...
}
Component.prototype.setState = function(partialState, callback){
    // ...
}
const React = {
    Component,
    // ...
}
// 使用
class index extends React.Component{
    // ...
}

點擊這里查看 React github源碼

面試官可以順著這個問JS繼承的相關問題,比如:ES6class繼承用ES5如何實現(xiàn)。據(jù)說很多人答得不好。

構造函數(shù)、原型對象和實例之間的關系

要弄懂extends繼承之前,先來復習一下構造函數(shù)、原型對象和實例之間的關系。
代碼表示:

function F(){}
var f = new F();
// 構造器
F.prototype.constructor === F; // true
F.__proto__ === Function.prototype; // true
Function.prototype.__proto__ === Object.prototype; // true
Object.prototype.__proto__ === null; // true

// 實例
f.__proto__ === F.prototype; // true
F.prototype.__proto__ === Object.prototype; // true
Object.prototype.__proto__ === null; // true

筆者畫了一張圖表示:

ES6 extends 繼承做了什么操作

我們先看看這段包含靜態(tài)方法的ES6繼承代碼:

// ES6
class Parent{
    constructor(name){
        this.name = name;
    }
    static sayHello(){
        console.log("hello");
    }
    sayName(){
        console.log("my name is " + this.name);
        return this.name;
    }
}
class Child extends Parent{
    constructor(name, age){
        super(name);
        this.age = age;
    }
    sayAge(){
        console.log("my age is " + this.age);
        return this.age;
    }
}
let parent = new Parent("Parent");
let child = new Child("Child", 18);
console.log("parent: ", parent); // parent:  Parent?{name: "Parent"}
Parent.sayHello(); // hello
parent.sayName(); // my name is Parent
console.log("child: ", child); // child:  Child?{name: "Child", age: 18}
Child.sayHello(); // hello
child.sayName(); // my name is Child
child.sayAge(); // my age is 18

其中這段代碼里有兩條原型鏈,不信看具體代碼。

// 1、構造器原型鏈
Child.__proto__ === Parent; // true
Parent.__proto__ === Function.prototype; // true
Function.prototype.__proto__ === Object.prototype; // true
Object.prototype.__proto__ === null; // true
// 2、實例原型鏈
child.__proto__ === Child.prototype; // true
Child.prototype.__proto__ === Parent.prototype; // true
Parent.prototype.__proto__ === Object.prototype; // true
Object.prototype.__proto__ === null; // true

一圖勝千言,筆者也畫了一張圖表示,如圖所示:


結(jié)合代碼和圖可以知道。
ES6 extends 繼承,主要就是:

把子類構造函數(shù)(Child)的原型(__proto__)指向了父類構造函數(shù)(Parent),

把子類實例child的原型對象(Child.prototype) 的原型(__proto__)指向了父類parent的原型對象(Parent.prototype)。

這兩點也就是圖中用不同顏色標記的兩條線。

子類構造函數(shù)Child繼承了父類構造函數(shù)Preant的里的屬性。使用super調(diào)用的(ES5則用call或者apply調(diào)用傳參)。

也就是圖中用不同顏色標記的兩條線。
看過《JavaScript高級程序設計-第3版》 章節(jié)6.3繼承的讀者應該知道,這2和3小點,正是寄生組合式繼承,書中例子沒有第1小點
1和2小點都是相對于設置了__proto__鏈接。那問題來了,什么可以設置了__proto__鏈接呢。

newObject.createObject.setPrototypeOf可以設置__proto__

說明一下,__proto__這種寫法是瀏覽器廠商自己的實現(xiàn)。
再結(jié)合一下圖和代碼看一下的newnew出來的實例的__proto__指向構造函數(shù)的prototype,這就是new做的事情。
摘抄一下之前寫過文章的一段。面試官問:能否模擬實現(xiàn)JS的new操作符,有興趣的讀者可以點擊查看。

new做了什么:

創(chuàng)建了一個全新的對象。

這個對象會被執(zhí)行[[Prototype]](也就是__proto__)鏈接。

生成的新對象會綁定到函數(shù)調(diào)用的this

通過new創(chuàng)建的每個對象將最終被[[Prototype]]鏈接到這個函數(shù)的prototype對象上。

如果函數(shù)沒有返回對象類型Object(包含Functoin, Array, Date, RegExg, Error),那么new表達式中的函數(shù)調(diào)用會自動返回這個新的對象。

Object.create ES5提供的

Object.create(proto, [propertiesObject])
方法創(chuàng)建一個新對象,使用現(xiàn)有的對象來提供新創(chuàng)建的對象的__proto__。
它接收兩個參數(shù),不過第二個可選參數(shù)是屬性描述符(不常用,默認是undefined)。對于不支持ES5的瀏覽器,MDN上提供了ployfill方案。
MDN Object.create()

// 簡版:也正是應用了new會設置__proto__鏈接的原理。
if(typeof Object.create !== "function"){
    Object.create = function(proto){
        function F() {}
        F.prototype = proto;
        return new F();
    }
}
Object.setPrototypeOf ES6提供的

Object.setPrototypeOf MDN

Object.setPrototypeOf() 方法設置一個指定的對象的原型 ( 即, 內(nèi)部[[Prototype]]屬性)到另一個對象或 null
Object.setPrototypeOf(obj, prototype)

`ployfill`
// 僅適用于Chrome和FireFox,在IE中不工作:
Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
  obj.__proto__ = proto;
  return obj; 
}

nodejs源碼就是利用這個實現(xiàn)繼承的工具函數(shù)的。
nodejs utils inherits

function inherits(ctor, superCtor) {
  if (ctor === undefined || ctor === null)
    throw new ERR_INVALID_ARG_TYPE("ctor", "Function", ctor);

  if (superCtor === undefined || superCtor === null)
    throw new ERR_INVALID_ARG_TYPE("superCtor", "Function", superCtor);

  if (superCtor.prototype === undefined) {
    throw new ERR_INVALID_ARG_TYPE("superCtor.prototype",
                                   "Object", superCtor.prototype);
  }
  Object.defineProperty(ctor, "super_", {
    value: superCtor,
    writable: true,
    configurable: true
  });
  Object.setPrototypeOf(ctor.prototype, superCtor.prototype);
}
ES6extendsES5版本實現(xiàn)

知道了ES6 extends繼承做了什么操作和設置__proto__的知識點后,把上面ES6例子的用ES5就比較容易實現(xiàn)了,也就是說實現(xiàn)寄生組合式繼承,簡版代碼就是:

// ES5 實現(xiàn)ES6 extends的例子
function Parent(name){
    this.name = name;
}
Parent.sayHello = function(){
    console.log("hello");
}
Parent.prototype.sayName = function(){
    console.log("my name is " + this.name);
    return this.name;
}

function Child(name, age){
    // 相當于super
    Parent.call(this, name);
    this.age = age;
}
// new
function object(){
    function F() {}
    F.prototype = proto;
    return new F();
}
function _inherits(Child, Parent){
    // Object.create
    Child.prototype = Object.create(Parent.prototype);
    // __proto__
    // Child.prototype.__proto__ = Parent.prototype;
    Child.prototype.constructor = Child;
    // ES6
    // Object.setPrototypeOf(Child, Parent);
    // __proto__
    Child.__proto__ = Parent;
}
_inherits(Child,  Parent);
Child.prototype.sayAge = function(){
    console.log("my age is " + this.age);
    return this.age;
}
var parent = new Parent("Parent");
var child = new Child("Child", 18);
console.log("parent: ", parent); // parent:  Parent?{name: "Parent"}
Parent.sayHello(); // hello
parent.sayName(); // my name is Parent
console.log("child: ", child); // child:  Child?{name: "Child", age: 18}
Child.sayHello(); // hello
child.sayName(); // my name is Child
child.sayAge(); // my age is 18

我們完全可以把上述ES6的例子通過babeljs轉(zhuǎn)碼成ES5來查看,更嚴謹?shù)膶崿F(xiàn)。

// 對轉(zhuǎn)換后的代碼進行了簡要的注釋
"use strict";
// 主要是對當前環(huán)境支持Symbol和不支持Symbol的typeof處理
function _typeof(obj) {
    if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
        _typeof = function _typeof(obj) {
            return typeof obj;
        };
    } else {
        _typeof = function _typeof(obj) {
            return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
        };
    }
    return _typeof(obj);
}
// _possibleConstructorReturn 判斷Parent。call(this, name)函數(shù)返回值 是否為null或者函數(shù)或者對象。
function _possibleConstructorReturn(self, call) {
    if (call && (_typeof(call) === "object" || typeof call === "function")) {
        return call;
    }
    return _assertThisInitialized(self);
}
// 如何 self 是void 0 (undefined) 則報錯
function _assertThisInitialized(self) {
    if (self === void 0) {
        throw new ReferenceError("this hasn"t been initialised - super() hasn"t been called");
    }
    return self;
}
// 獲取__proto__
function _getPrototypeOf(o) {
    _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
        return o.__proto__ || Object.getPrototypeOf(o);
    };
    return _getPrototypeOf(o);
}
// 寄生組合式繼承的核心
function _inherits(subClass, superClass) {
    if (typeof superClass !== "function" && superClass !== null) {
        throw new TypeError("Super expression must either be null or a function");
    }
    // Object.create()方法創(chuàng)建一個新對象,使用現(xiàn)有的對象來提供新創(chuàng)建的對象的__proto__。 
    // 也就是說執(zhí)行后 subClass.prototype.__proto__ === superClass.prototype; 這條語句為true
    subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: {
            value: subClass,
            writable: true,
            configurable: true
        }
    });
    if (superClass) _setPrototypeOf(subClass, superClass);
}
// 設置__proto__
function _setPrototypeOf(o, p) {
    _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
        o.__proto__ = p;
        return o;
    };
    return _setPrototypeOf(o, p);
}
// instanceof操作符包含對Symbol的處理
function _instanceof(left, right) {
    if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
        return right[Symbol.hasInstance](left);
    } else {
        return left instanceof right;
    }
}

function _classCallCheck(instance, Constructor) {
    if (!_instanceof(instance, Constructor)) {
        throw new TypeError("Cannot call a class as a function");
    }
}
// 按照它們的屬性描述符 把方法和靜態(tài)屬性賦值到構造函數(shù)的prototype和構造器函數(shù)上
function _defineProperties(target, props) {
    for (var i = 0; i < props.length; i++) {
        var descriptor = props[i];
        descriptor.enumerable = descriptor.enumerable || false;
        descriptor.configurable = true;
        if ("value" in descriptor) descriptor.writable = true;
        Object.defineProperty(target, descriptor.key, descriptor);
    }
}
// 把方法和靜態(tài)屬性賦值到構造函數(shù)的prototype和構造器函數(shù)上
function _createClass(Constructor, protoProps, staticProps) {
    if (protoProps) _defineProperties(Constructor.prototype, protoProps);
    if (staticProps) _defineProperties(Constructor, staticProps);
    return Constructor;
}

// ES6
var Parent = function () {
    function Parent(name) {
        _classCallCheck(this, Parent);
        this.name = name;
    }
    _createClass(Parent, [{
        key: "sayName",
        value: function sayName() {
            console.log("my name is " + this.name);
            return this.name;
        }
    }], [{
        key: "sayHello",
        value: function sayHello() {
            console.log("hello");
        }
    }]);
    return Parent;
}();

var Child = function (_Parent) {
    _inherits(Child, _Parent);
    function Child(name, age) {
        var _this;
        _classCallCheck(this, Child);
        // Child.__proto__ => Parent
        // 所以也就是相當于Parent.call(this, name); 是super(name)的一種轉(zhuǎn)換
        // _possibleConstructorReturn 判斷Parent.call(this, name)函數(shù)返回值 是否為null或者函數(shù)或者對象。
        _this = _possibleConstructorReturn(this, _getPrototypeOf(Child).call(this, name));
        _this.age = age;
        return _this;
    }
    _createClass(Child, [{
        key: "sayAge",
        value: function sayAge() {
            console.log("my age is " + this.age);
            return this.age;
        }
    }]);
    return Child;
}(Parent);

var parent = new Parent("Parent");
var child = new Child("Child", 18);
console.log("parent: ", parent); // parent:  Parent?{name: "Parent"}
Parent.sayHello(); // hello
parent.sayName(); // my name is Parent
console.log("child: ", child); // child:  Child?{name: "Child", age: 18}
Child.sayHello(); // hello
child.sayName(); // my name is Child
child.sayAge(); // my age is 18

如果對JS繼承相關還是不太明白的讀者,推薦閱讀以下書籍的相關章節(jié),可以自行找到相應的pdf版本。

推薦閱讀JS繼承相關的書籍章節(jié)

《JavaScript高級程序設計第3版》-第6章 面向?qū)ο蟮某绦蛟O計,6種繼承的方案,分別是原型鏈繼承、借用構造函數(shù)繼承、組合繼承、原型式繼承、寄生式繼承、寄生組合式繼承。圖靈社區(qū)本書地址,后文放出github鏈接,里面包含這幾種繼承的代碼demo

《JavaScript面向?qū)ο缶幊痰?版》-第6章 繼承,12種繼承的方案。1.原型鏈法(仿傳統(tǒng))、2.僅從原型繼承法、3.臨時構造器法、4.原型屬性拷貝法、5.全屬性拷貝法(即淺拷貝法)、6.深拷貝法、7.原型繼承法、8.擴展與增強模式、9.多重繼承法、10.寄生繼承法、11.構造器借用法、12.構造器借用與屬性拷貝法。

ES6標準入門-第21章class的繼承

《深入理解ES6》-第9章 JavaScript中的類

《你不知道的JavaScript-上卷》第6章 行為委托和附錄A ES6中的class

總結(jié)

繼承對于JS來說就是父類擁有的方法和屬性、靜態(tài)方法等,子類也要擁有。子類中可以利用原型鏈查找,也可以在子類調(diào)用父類,或者從父類拷貝一份到子類等方案。
繼承方法可以有很多,重點在于必須理解并熟
悉這些對象、原型以及構造器的工作方式,剩下的就簡單了。寄生組合式繼承是開發(fā)者使用比較多的。
回顧寄生組合式繼承。主要就是三點:

子類構造函數(shù)的__proto__指向父類構造器,繼承父類的靜態(tài)方法

子類構造函數(shù)的prototype__proto__指向父類構造器的prototype,繼承父類的方法。

子類構造器里調(diào)用父類構造器,繼承父類的屬性。

行文到此,文章就基本寫完了。文章代碼和圖片等資源放在這里github inhert和demo展示es6-extends,結(jié)合console、source面板查看更佳。

讀者發(fā)現(xiàn)有不妥或可改善之處,歡迎評論指出。另外覺得寫得不錯,可以點贊、評論、轉(zhuǎn)發(fā),也是對筆者的一種支持。

關于

作者:常以若川為名混跡于江湖。前端路上 | PPT愛好者 | 所知甚少,唯善學。
個人博客
segmentfault前端視野專欄,開通了前端視野專欄,歡迎關注
掘金專欄,歡迎關注
知乎前端視野專欄,開通了前端視野專欄,歡迎關注
github,歡迎follow~

文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/101904.html

相關文章

  • 面試官問JSthis指向

    摘要:之前寫過一篇文章面試官問能否模擬實現(xiàn)的和方法就是利用對象上的函數(shù)指向這個對象,來模擬實現(xiàn)和的。雖然實際使用時不會顯示返回,但面試官會問到。非嚴格模式下,和,指向全局對象 前言 面試官出很多考題,基本都會變著方式來考察this指向,看候選人對JS基礎知識是否扎實。讀者可以先拉到底部看總結(jié),再谷歌(或各技術平臺)搜索幾篇類似文章,看筆者寫的文章和別人有什么不同(歡迎在評論區(qū)評論不同之處),...

    warnerwu 評論0 收藏0
  • 學習 underscore 源碼整體架構,打造屬于自己函數(shù)式編程類庫

    摘要:譯立即執(zhí)行函數(shù)表達式處理支持瀏覽器環(huán)境微信小程序。學習整體架構,利于打造屬于自己的函數(shù)式編程類庫。下一篇文章可能是學習的源碼整體架構。也可以加微信,注明來源,拉您進前端視野交流群。 前言 上一篇文章寫了jQuery整體架構,學習 jQuery 源碼整體架構,打造屬于自己的 js 類庫 雖然看過挺多underscore.js分析類的文章,但總感覺少點什么。這也許就是紙上得來終覺淺,絕知此...

    junnplus 評論0 收藏0
  • 18年求職面經(jīng)及總結(jié)

    摘要:年求職面經(jīng)及總結(jié)我的求職之路差不多走到盡頭了感覺真是精疲力盡了把這大半年的經(jīng)歷和面試總結(jié)寫下來希望能給和我一樣在求職路上煎熬的人一點幫助先說背景微電子科學與工程專業(yè)學過兩門和相關的課程語言和單片機這個專業(yè)的唯一好處就是大部分人并不知道這個專 18年求職面經(jīng)及總結(jié) 我的求職之路差不多走到盡頭了,感覺真是精疲力盡了.把這大半年的經(jīng)歷和面試總結(jié)寫下來,希望能給和我一樣在求職路上煎熬的人一點幫...

    zhangwang 評論0 收藏0
  • 18年求職面經(jīng)及總結(jié)

    摘要:年求職面經(jīng)及總結(jié)我的求職之路差不多走到盡頭了感覺真是精疲力盡了把這大半年的經(jīng)歷和面試總結(jié)寫下來希望能給和我一樣在求職路上煎熬的人一點幫助先說背景微電子科學與工程專業(yè)學過兩門和相關的課程語言和單片機這個專業(yè)的唯一好處就是大部分人并不知道這個專 18年求職面經(jīng)及總結(jié) 我的求職之路差不多走到盡頭了,感覺真是精疲力盡了.把這大半年的經(jīng)歷和面試總結(jié)寫下來,希望能給和我一樣在求職路上煎熬的人一點幫...

    fjcgreat 評論0 收藏0

發(fā)表評論

0條評論

stonezhu

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<