Junwen's home
  • ES6

    • ES6 Decorator
    • ES6核心特性
    • Promise&Generator
  • js原理

    • 简单实现bind、apply和call
    • 如何遍历一个dom tree
    • 实现函数currying
    • 实现一个event
    • 详解js的继承
    • 详解requestAnimationFrame
    • Canvas api详解
    • DOM事件
    • EventLoop详解
    • JavaScript的内存管理
    • JavaScript的运行机制
    • Math对象
    • new操作符都做了什么
    • create基本实现原理
    • Set、Map、WeakSet和WeakMap
    • web worker原理
    • WebGL教程(MDN)
  • jsInfoSeries

    • 简介
    • JavaScript基础知识
    • 基础知识2
    • 基础知识3
    • 基础知识4
  • 技巧

    • 5个js解构有趣用途
    • 如何使用set提高代码性能
    • cordova构建项目时的问题
    • js中轻松遍历对象属性的几种方式
  • 怎么写出更好的css
  • BFC详解
  • box-shadow详解
  • CSS小技巧
  • Grid布局详解
HTML
  • IP十问
  • http笔试
  • http协议
  • 浏览器原理
  • 浏览器缓存其实就这么一回事儿
  • 浏览器兼容性问题
  • 移动端开发兼容性适配
  • 前端性能优化
  • 前端如何进行seo优化
  • webpack

    • webpack HMR
    • webpack优化基本方法
  • leetcode题解

    • 两数之和
    • 判断整数是否为回文串
    • 无重复字符的最长子串
  • Js链表
  • JavaScript排序
  • React

    • 虚拟DOM原理理解
    • React Hook
    • 组件复用指南
  • Vue

    • Vue举一反三
面试题
读书笔记
GitHub (opens new window)

Syun0216

多读书多种树
  • ES6

    • ES6 Decorator
    • ES6核心特性
    • Promise&Generator
  • js原理

    • 简单实现bind、apply和call
    • 如何遍历一个dom tree
    • 实现函数currying
    • 实现一个event
    • 详解js的继承
    • 详解requestAnimationFrame
    • Canvas api详解
    • DOM事件
    • EventLoop详解
    • JavaScript的内存管理
    • JavaScript的运行机制
    • Math对象
    • new操作符都做了什么
    • create基本实现原理
    • Set、Map、WeakSet和WeakMap
    • web worker原理
    • WebGL教程(MDN)
  • jsInfoSeries

    • 简介
    • JavaScript基础知识
    • 基础知识2
    • 基础知识3
    • 基础知识4
  • 技巧

    • 5个js解构有趣用途
    • 如何使用set提高代码性能
    • cordova构建项目时的问题
    • js中轻松遍历对象属性的几种方式
  • 怎么写出更好的css
  • BFC详解
  • box-shadow详解
  • CSS小技巧
  • Grid布局详解
HTML
  • IP十问
  • http笔试
  • http协议
  • 浏览器原理
  • 浏览器缓存其实就这么一回事儿
  • 浏览器兼容性问题
  • 移动端开发兼容性适配
  • 前端性能优化
  • 前端如何进行seo优化
  • webpack

    • webpack HMR
    • webpack优化基本方法
  • leetcode题解

    • 两数之和
    • 判断整数是否为回文串
    • 无重复字符的最长子串
  • Js链表
  • JavaScript排序
  • React

    • 虚拟DOM原理理解
    • React Hook
    • 组件复用指南
  • Vue

    • Vue举一反三
面试题
读书笔记
GitHub (opens new window)
  • ES6

    • ES6 Decorator
    • ES6核心特性
    • Promise&Generator
  • js原理

    • 简单实现bind、apply和call
    • 如何遍历一个dom tree
    • 实现函数currying
    • 实现一个event
    • 详解js的继承
      • 基于对象的继承
      • 基于类型的继承(Klass)
    • 详解requestAnimationFrame
    • Canvas api详解
    • DOM事件
    • EventLoop详解
    • JavaScript的内存管理
    • JavaScript的运行机制
    • Math对象
    • new操作符都做了什么
    • Object.create基本实现原理
    • Set、Map、WeakSet和WeakMap
    • web worker原理
    • WebGL教程(MDN)
  • jsInfoSeries

    • 简介
    • JavaScript基础知识
    • 基础知识2
    • 基础知识3
    • 基础知识4
  • 技巧

    • 5个js解构有趣用途
    • 如何使用set提高代码性能
    • cordova构建项目时的问题
    • js中轻松遍历对象属性的几种方式
  • JavaScript
junwen
2019-11-05

详解js的继承

JavaScript的继承可以分为两类:

  • 基于对象的继承
  • 基于类型的继承

# 基于对象的继承

也叫原型继承。我们知道js字面量创建的对象都会连接到Object.prototype,因此我们用Object.prototype来实现继承。使用Object.create直接让新对象继承就对象的属性。例如

var person = {
  name: 'Miro',
  getName: function() {return this.name}
}

var p1 = Object.create(person);
console.log(p1.getName()); // Miro
1
2
3
4
5
6
7

Object.create()还可以指定第二个参数,即数据属性,将其添加到新对象中。数据属性可设4个描述符value, writable,enumerable,configurable 。后3个看名字也能猜出意思,不指定的话默认为false。例如:

var p2 = Object.create(person, {
  name: {
    value: 'Syun'
  }
})
console.log(p2.getName()); // Syun
1
2
3
4
5
6

用Object.create相当于创建了一个全新的对象,你可以给该对象任意新增,重载它的属性和方法

var person = {
  name: 'Miro',
  getName: function() {return this.name;},
  getAge: function() {return this.age} // person上并没有这个age
}

var p3 = Obkect.create(person)
;
p3.age = 17;
p3.location = '深圳';
p3.getLocation = function() {return this.location}

console.log(p3.getName()) // Miro
console.log(p3.getAge()) // 17
console.log(p3.getLocation()) // 深圳

console.log(person.getAge()) // undefined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 实现一个Object.create

_create = function (o) {
    let F = function () {}
    F.prototype = o
    return new F()
}
1
2
3
4
5

# 基于类型的继承(Klass)

# 模式一: 将原型对象指向父对象

Child.prototype = new Parent(); // 原型对象指向父对象
1

例子:

function Parent(n) {     //父构造函数
    this.name = n || 'Adam';
}
Parent.prototype.say = function() {
    return this.name;
}
function Child(n) {}     //空白的子构造函数

function inherit(Child, Parent) {
    Child.prototype = new Parent(); //原型对象指向父对象
}

inherit(Child, Parent); //继承

var c1 = new Child("Jack");
console.log(c1.name);   //Adam
c1.say();               //Adam
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

缺点:子构造函数的参数不能传给父构造函数,即使你保证父子构造函数都不需要参数,但是效率也是底下的,既每个子对象都会重复地创建父对象,效率不高。

# 模式二:借用构造函数

function Child(a, b, c, d) {        //子构造函数
    Parent.apply(this, arguments);  //借用父构造函数
}
1
2
3

例子:

function Parent(n) {     //父构造函数
    this.name = n || 'Adam';
}
Parent.prototype.say = function() {
    return this.name;
}

function Child(n) {     //子构造函数
    Parent.apply(this, arguments);  //借用父构造函数
}

var c2 = new Child("Patrick");
console.log(c2.name);   //Patrick
c2.say();               //error,未定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14

总结一下该模式:子类只是借用了父类构造函数的实现,从结果上看,获得了一个父对象的副本。但子类对象和父类对象是完全独立的,不存在修改子类对象的属性值影响父对象的风险。缺点是该模式某种意义上讲,其实不是继承,无法从父类的prototype中获得任何东西

# 模式三:借用和设置原型

function Child(a, b, c, d) {          //子构造函数
    Parent.apply(this, arguments);  //参照模式二,借用父构造函数
}
Child.prototype = new Parent();     //参照模式一,将原型对象指向父对象
1
2
3
4

该模式通常用用就可以了,但不是完美的。缺点和模式二的缺点二一样,多个子对象都会重复地创建父对象,效率不高。

# 模式四:共享原型

function inherit(Child, Parent) {
    Child.prototype = Parent.prototype;
}
1
2
3

# 模式五:临时构造函数

function inherit(Child, Parent) {
    var F = function() {};      //空的临时构造函数
    F.prototype = Parent.prototype;
    Child.prototype = new F();
}
1
2
3
4
5

与模式四的差别就是,新定义了个空的临时构造函数F(),子类的原型指向该临时构造函数。这样修改子类原型时,实际修改的是修改到了临时构造函数F(),不会影响父类:

加分项一:添加一个指向父类原型的引用,例如其他语言里的super:

function inherit(Child, Parent) {
    var F = function() {};       //空的临时构造函数
    F.prototype = Parent.prototype;
    Child.prototype = new F();
    Child.uber = Parent.prototype; //uber表示super,因为super是保留的关键字
}
1
2
3
4
5
6

加分项二:重置该构造函数的指针,以免在将来某个时候还需要该构造函数。如果不重置构造函数的指针,那么所有子对象会报告Parent()是它们的构造函数,这没有任何用处:

function inherit(Child, Parent) {
    var F = function() {};          //空的临时构造函数
    F.prototype = Parent.prototype;
    Child.prototype = new F();
    Child.uber = Parent.prototype;  //uber表示super,因为super是保留的关键字
    Child.prototype.constructor = Child;    //修正constructor属性
}
1
2
3
4
5
6
7

加分项三:临时构造函数F()不必每次继承时都创建,仅创建一次以提高效率:

var inherit = (function() {
    var F = function() {};
    return function(Child, Parent) {
        F.prototype = Parent.prototype;
        Child.prototype = new F();
        Child.uber = Parent.prototype;
        Child.prototype.constructor = Child;
    }
}());
1
2
3
4
5
6
7
8
9
在Github上编辑此页 (opens new window)
#JavaScript
上次更新: 3/22/2021, 3:47:15 AM
实现一个event
详解requestAnimationFrame

← 实现一个event 详解requestAnimationFrame→

最近更新
01
如何打造全链路项目生命周期的统一交付平台
04-10
02
如何建立前端标准化研发流程
04-10
03
如何从0到1一步步成体系地搭建CI
04-10
更多文章>
Theme by Vdoing | Copyright © 2019-2021 Syun
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式