一、对象冒充

其原理如下:构造函数使用 this 关键字给所有属性和方法赋值(即采用类声明的构造函数方式)。因为构造函数只是一个函数,所以可使 Parent 构造函数
成为 Children 的方法,然后调用它。Children 就会收到 Parent 的构造函数中定义的属性和方法。例如,用下面的方式定义 Parent 和 Children:

  1. ]// 父类构造函数
  2. var Parent = function(name){
  3. this.name = name;
  4.  
  5. this.sayHi = function(){
  6. console.log("Hi! " + this.name + ".");
  7. }
  8. };
  9.  
  10. // 子类构造函数
  11. var Children = function(name){
  12. this.method = Parent;
  13. this.method(name); // 实现继承的关键
  14. delete this.method;
  15.  
  16. this.getName = function(){
  17. console.log(this.name);
  18. }
  19. };
  20.  
  21. var p = new Parent("john");
  22. var c = new Children("joe");
  23.  
  24. p.sayHi(); // 输出: Hi! john.
  25. c.sayHi(); // 输出: Hi! joe.
  26. c.getName(); // 输出: jo

原理:就是把 Parent 构造函数放到 Children 构造函数里面执行一次。那为什么不直接执行,非要转个弯把 Parent 赋值给 Children 的 method 属性再执行呢?
这跟 this 的指向有关,在函数内 this 是指向 window 的。当将 Parent 赋值给 Children 的 method 时, this 就指向了 Children 类的实例。

二、原型链继承

众所周知,JavaScript 是一门基于原型的语言,在 JavaScript 中 prototype 对象的任何属性和方法都被传递给那个类的所有实例。原型链利用这种功能来实现继承机制:

  1. // 父类构造函数
  2. var Parent = function(){
  3. this.name = "john";
  4.  
  5. this.sayHi = function(){
  6. console.log("Hi! " + this.name + ".");
  7. }
  8. };
  9.  
  10. // 子类构造函数
  11. var Children = function(){};
  12.  
  13. Children.prototype = new Parent(); // 实现继承的关键
  14.  
  15. var p = new Parent();
  16. var c = new Children();
  17.  
  18. p.sayHi(); // 输出: Hi! john.
  19. c.sayHi(); // 输出: Hi! john.

注意:调用 Parent 的构造函数,没有给它传递参数。这在原型链中是标准做法。要确保构造函数没有任何参数。

三、使用 call 或 applay 方法

这个方法是与对象冒充方法最相似的方法,因为它也是通过改变了 this 的指向而实现继承:

  1. // 父类构造函数
  2. var Parent = function(name){
  3. this.name = name;
  4.  
  5. this.sayHi = function(){
  6. console.log("Hi! " + this.name + ".");
  7. }
  8. };
  9.  
  10. // 子类构造函数
  11. var Children = function(name){
  12. Parent.call(this, name); // 实现继承的关键
  13.  
  14. this.getName = function(){
  15. console.log(this.name);
  16. }
  17. };
  18.  
  19. var p = new Parent("john");
  20. var c = new Children("joe");
  21.  
  22. p.sayHi(); // 输出: Hi! john.
  23. c.sayHi(); // 输出: Hi! john.
  24. c.getName(); // 输出: joe

apply 方法本人就不举列了,它和 call 方法的区别在于它的第二个参数必须是数组。

四、混合方式

对象冒充的主要问题是必须使用构造函数方式,这不是最好的选择。不过如果使用原型链,就无法使用带参数的构造函数了。如何选择呢?答案很简单,两者都用。
在 JavaScript 中创建类的最好方式是用构造函数定义属性,用原型定义方法。这种方式同样适用于继承机制:

  1. // 父类构造函数
  2. var Parent = function(name){
  3. this.name = name;
  4. };
  5.  
  6. Parent.prototype.sayHi = function(){
  7. console.log("Hi! " + this.name + ".");
  8. };
  9.  
  10. // 子类构造函数
  11. var Children = function(name, age){
  12. Parent.call(this, name); // 实现继承的关键
  13. this.age = age;
  14. };
  15.  
  16. Children.prototype = new Parent(); // 实现继承的关键
  17.  
  18. Children.prototype.getAge = function(){
  19. console.log(this.age);
  20. };
  21.  
  22. var p = new Parent("john");
  23. var c = new Children("joe",30);
  24.  
  25. p.sayHi(); // 输出: Hi! john.
  26. c.sayHi(); // 输出: Hi! joe.
  27. c.getAge(); // 输出: 30

五、使用Object.create 方法

Object.create 方法会使用指定的原型对象及其属性去创建一个新的对象:

  1. // 父类构造函数
  2. var Parent = function(name){
  3. this.name = name;
  4. };
  5.  
  6. Parent.prototype.sayHi = function(){
  7. console.log("Hi! " + this.name + ".");
  8. };
  9.  
  10. // 子类构造函数
  11. var Children = function(name, age){
  12. Parent.call(this, name); // 实现继承的关键
  13. this.age = age;
  14. };
  15.  
  16. Children.prototype = Object.create(Parent.prototype); // 实现继承的关键
  17. Children.prototype.constructor = Children; // @
  18.  
  19. Children.prototype.getAge = function(){
  20. console.log(this.age);
  21. };
  22.  
  23. var p = new Parent("john");
  24. var c = new Children("joe",30);
  25.  
  26. p.sayHi(); // 输出: Hi! john.
  27. c.sayHi(); // 输出: Hi! joe.
  28. c.getAge(); // 输出: 30

@ 当执行 Children.prototype = Object.create(Parent.prototype) 这个语句后,Children 的 constructor 就被改变为 Parent ,因此需要将 Children.prototype.constructor 重
新指定为 Children 自身。

六、extends 关键字实现继承

这个是 ES6 的语法糖,下面看下es6实现继承的方法:

  1. class Parent {
  2. constructor(name, age) {
  3. this.name = name;
  4. this.age = age;
  5. }
  6. }
  7.  
  8. class Children extends Parent {
  9. constructor(name, age, job) {
  10. this.job = job; // 这里会报错
  11. super(name, age);
  12. this.job = job; // 正确
  13. }
  14. }

上面代码中,子类的constructor方法没有调用super之前,就使用this关键字,结果报错,而放在super方法之后就是正确的。子类Children的构造函数之中的super(),代表调用父类Parent的构造函数。这是必须的,否则 JavaScript 引擎会报错。

注意,super虽然代表了父类Parent的构造函数,但是返回的是子类Children的实例,即super内部的this指的是Children,因此super()在这里相当于Parent.prototype.constructor.call(this)

详解JavaScript对象继承方式的更多相关文章

  1. 详解Javascript的继承实现(二)

    上文<详解Javascript的继承实现>介绍了一个通用的继承库,基于该库,可以快速构建带继承关系和静态成员的javascript类,好使用也好理解,额外的好处是,如果所有类都用这种库来构 ...

  2. js对象详解(JavaScript对象深度剖析,深度理解js对象)

    js对象详解(JavaScript对象深度剖析,深度理解js对象) 这算是酝酿很久的一篇文章了. JavaScript作为一个基于对象(没有类的概念)的语言,从入门到精通到放弃一直会被对象这个问题围绕 ...

  3. 详解Javascript的继承实现

    我最早掌握的在js中实现继承的方法是在w3school学到的混合原型链和对象冒充的方法,在工作中,只要用到继承的时候,我都是用这个方法实现.它的实现简单,思路清晰:用对象冒充继承父类构造函数的属性,用 ...

  4. 一文详解JavaScript的继承模式

    1 原型链继承 #### ES6中通过原型继承多个引用类型的属性和方法,由于原型和实例的关系,即每个构造函数都有自己的原型对象,同时原型有一个属性指向构造函数,并且实例有一个内部的指针指向原型.如果存 ...

  5. JavaScript对象继承方式

    一.对象冒充 其原理如下:构造函数使用 this 关键字给所有属性和方法赋值(即采用类声明的构造函数方式).因为构造函数只是一个函数,所以可使 Parent 构造函数 成为 Children 的方法, ...

  6. 详解javascript中的this对象

    详解javascript中的this对象 前言 Javascript是一门基于对象的动态语言,也就是说,所有东西都是对象,一个很典型的例子就是函数也被视为普通的对象.Javascript可以通过一定的 ...

  7. JavaScript 的对象继承方式,有几种写法?

    JavaScript 的对象继承方式,有几种写法? 一.对象冒充 其原理如下:构造函数使用 this 关键字给所有属性和方法赋值(即采用类声明的构造函数方式).因为构造函数只是一个函数,所以可使 Pa ...

  8. 架构师JavaScript 的对象继承方式,有几种程序写法?

    架构师JavaScript 的对象继承方式,有几种程序写法?   一.对象冒充 其原理如下:构造函数使用 this 关键字给所有属性和方法赋值(即采用类声明的构造函数方式).因为构造函数只是一个函数, ...

  9. 【转】详解JavaScript中的this

    ref:http://blog.jobbole.com/39305/ 来源:foocoder 详解JavaScript中的this JavaScript中的this总是让人迷惑,应该是js众所周知的坑 ...

随机推荐

  1. sql server 2008 sql prompt 自动提示

    sql server 2008 在编写SQL脚本的时候,总是希望能提示一些信息,或者自动提示需要查询的表的名字,或者表的基本信息,sql server默认会有一些提示的,如果没有可以设置工具--> ...

  2. 算法精解:DAG有向无环图

    DAG是公认的下一代区块链的标志.本文从算法基础去研究分析DAG算法,以及它是如何运用到区块链中,解决了当前区块链的哪些问题. 关键字:DAG,有向无环图,算法,背包,深度优先搜索,栈,BlockCh ...

  3. 一次日语翻译的Chrome插件开发经历

    序言 去年7月刚过了日语N2,想着今年考个N1,为了加深日语文化的了解,还有学习日语,平时免不了经常上日语网站. 但是毕竟水平有限,所以不免遇到不认识的单词,日语单词的一个特点就是很多单词你知道是什么 ...

  4. 关于Eclipse无法识别手机或者模拟器的解决方案

    Android开发的时候经常会出现eclipse devices中不显示手机或模拟器的情况 网上有很多方法,但是都不实用.这里我提供一种方法: 如果手机连接上了不显示的话首先我们要确定我们手机的驱动是 ...

  5. guava cache使用和源码分析

    guava cache的优点和使用场景,用来判断业务中是否适合使用此缓存 介绍常用的方法,并给出示例,作为使用的参考 深入解读源码. guava简介 guava cache是一个本地缓存.有以下优点: ...

  6. 基于Jmeter的自动化测试实施方案设计

    前言: Jmeter是目前最流行的一种测试工具,基于此工具我们搭建了一整套的自动化方案,包括了脚本添加配置.本地配置和运行.服务器配置等内容,完成了自动化测试闭环,通过这种快捷简便高效的方式,希望可以 ...

  7. 1-3 Spring Bean 的属性值设置

    详见http://www.cnblogs.com/chenssy/archive/2013/03/17/2964593.html 1.注入普通的属性值 <bean id="Cat&qu ...

  8. Lucene-01:创建索引

    我们在D盘下建一个文件夹叫lucene,lucene内再建两个文件夹,一个叫example,一个叫index01.example文件夹下三个txt文件,a.txt内容为hello java,b.txt ...

  9. Java多线程JUC

    1. volatile 关键字 多线程访问的时候,一个比较严重的问题就是内存不可见,其实在内存访问的时候每一个线程都有一个自己的缓冲区,每次在做修改的时候都是从主存取到数据,然后放到自己的缓冲区中,在 ...

  10. Java飞机大战源代码

    刚学不久java,做了一个飞机大战的小小小小游戏,现在把这个思路总结以及代码分享出来.大佬别吐槽(emmmmmm .....开发环境:jdk1.7 开发工具:eclipese PlanelJPanel ...