[JavaScript原型继承理解一]

转:http://www.cnblogs.com/harolei/p/3740354.html

对于JavaScript的继承和原型链,虽然之前自己看了书也听了session,但还是一直觉得云里雾里,不禁感叹JavaScript真是一门神奇的语言。这次经过Sponsor的一对一辅导和自己回来后反复思考,总算觉得把其中的精妙领悟一二了。

1. JavaScript创建对象

在面向对象语言中,通常通过定义类然后再进行实例化来创建多个具有相同属性和方法的对象。但是在JavaScript中并没有类的概念,不过ECMAScript中的构造函数可以用来创建特定类型的对象。因此,在JavaScript中可以创建自定义的构造函数,并且通过new操作符来创建对象。

在JavaScript中并没有“指定的”构造函数类型,构造函数实质上也是函数,它和一般函数的区别只在于调用方式不同。只有当通过new操作符来调用的时候它才可以作为构造函数来创建对象实例,并且把构造函数的作用域赋给这个新对象(将this指向这个新对象)。如果没有使用new来调用构造函数,那就是普通的函数调用,这个时候this指向的是window对象,这样做会导致所有的属性和方法被添加到全局,因此一定要注意命名构造函数时首字母大写,并且永远使用new来调用它。


function Person(name, gender) {
this.name = name;
this.gender = gender;    this.say = function() {
    console.log("Hello");
  }
}

var person1 = new Person("Mike", "male");
var person2 = new Person("Kate", "female");

这段代码就定义了一个构造函数Person,
并且给它添加了name和gender属性以及say方法。通过调用new操作符来创建了两个Person的实例person1和person2.可以通过代码来验证一下:


person1 instanceof Person; //true;
person2 instanceof Person; //true;

并且person1和person2都分别具有了name,gender属性,并且都被附上了构造对象时传入的值。同时它们也都具有say方法。

不过通过比较可以看出来,虽然这时person1和person2都具有say方法,但它们其实并不是同一个Function的实例,也就是说当使用new来创建构造函数的实例时,每个方法都在实例上重新被创建了一遍:


person1.say == person2.say; //false

这样的重复创建Function是没有必要的,甚至在实例变多的时候造成一种浪费。为此,我们可以使用构造函数的prototype属性来解决问题。prototype原型对象是用来寻访继承特征的地方,添加到prototype对象中的属性和方法都会被构造函数创建的实例继承,这时实例中的方法就都是指向原型对象中Function的引用了:


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

Person.prototype.say = function() {
console.log("Hello");
}

var person1 = new Person("Mike", "male");
var person2 = new Person("Kate", "female");

person1.say == person2.say //true

2. prototype, constructor, 和__proto__

构造函数,原型对象,实例的关系是:JavaScript中,每个函数都有一个prototype属性,这是一个指针,指向了这个函数的原型对象。而这个原型对象有一个constructor属性,指向了该构造函数。每个通过该构造函数创建的对象都包含一个指向原型对象的内部指针__proto__。

用代码表示它们的关系:


Person.prototype.constructor === Person;
person1.__proto__ === Person.prototype;
person2.__proto__ === Person.prototype;

3. 继承的实现

JavaScript中使用原型链作为实现继承的主要方法。由于对象实例拥有一个指向原型对象的指针,而当这个原型对象又等于另一个类型的实例时,它也具有这个指向另一类型原型对象的指针。因此通过指向原型对象的指针__proto__就可以层层递进的找到原型对象,这就是原型链。通过原型链来完成继承:


function Teacher(title) {
this.title = title;
}
Teacher.prototype = new Person();

var teacher = new Teacher("professor");

这时,我们通过将Teacher的prototype原型对象指向Person的实例来完成了Teacher对Person的继承。可以看到Teacher的实例teacher具有了Person的属性和方法。

但是如果只是将构造函数的prototype原型对象指向另一对象实例,发生的事情其实可以归纳为:


Teacher.prototype instanceof Person //true
Teacher.prototype.constructor == Person //true
Teacher.prototype.__proto__ === Person.prototype //true

问题出现了:这时Teacher的构造函数变成了Person。虽然我们在使用创建的实例的属性和方法的时候constructor的类型并不会产生很大的影响,但是这依然是一个很不合理的地方。因此一般在使用原型链实现继承时,在将prototype指向另一个构造函数的实例之后需要再将当前构造函数的constructor改回来:


Teacher.prototype = new Person();
Teacher.prototype.constructor = Teacher;

这样才是真正的实现了原型链继承并且不改变当前构造函数和原型对象的关系:

到这里,我们就可以将这个继承过程封装成一个extend方法来专门完成继承的工作了:


var extend = function(Child, Parent) {
Child.prototype = new Parent();
Child.prototype.constructor = Child;
return new Child();
};

现在这个方法接受两个参数:Child和Parent,并且在完成继承之后实例化一个Child对象并返回。我们当然可以根据需要来继续丰富这个函数,包括实例化的时候需要传入的参数什么的。

[JavaScript原型继承理解一],布布扣,bubuko.com

时间: 2024-10-07 06:32:23

[JavaScript原型继承理解一]的相关文章

彻底理解Javascript原型继承

彻底理解Javascript原型继承 之前写过一篇Javascript继承主题的文章,这篇文章作为一篇读书笔记,分析的不够深入. 本文试图进一步思考,争取彻底理解Javascript继承原理 实例成员与原型成员 举一个<高性能Javascript>书中例子 var book={ title :"High Performance JavaScript", publisher:"Yahoo!Press" }; alert(book.toString());/

JavaScript原型继承的陷阱

JavaScript原型继承的陷阱 JavaScript默认采用原型继承.虽然没有类(class)的概念,它的函数(function)可以充当构造器(constructor).构造器结合this,new可以构建出类似Java的类.因此,JavaScript通过扩展自身能模拟类式(class-based)继承. JavaScript和其它面向对象语言一样,对象类型采用引用方式.持有对象的变量只是一个地址,而基本类型数据是值.当原型上存储对象时,就可能有一些陷阱. 先看第一个例子 var creat

JavaScript 原型继承开端

1.原型继承本质       就javascript对象系统的实现来讲,对象并没有原型,而构造器有原型(构造器.prototype指向其原型).对象只有构造自某个原型的说法,并没有持有某个原型的说法.原型其实也是一个对象实例.原型的含义是指:如果构造器有一个原型对象A,则由该构造器创建的实例都必然复制自A. 2.空的对象是所有对象的基础 来看看以下代码: 1 <html> 2 <head> 3 <meta http-equiv="content-type"

javascript原型的理解

JavaScript是一门面向对象的语言.在JavaScript中有一句很经典的话,万物皆对象.既然是面向对象的,那就有面向对象的三大特征:封装.继承.多态.这里讲的是JavaScript的继承,其他两个容后再讲. JavaScript的继承和C++的继承不大一样,C++的继承是基于类的,而JavaScript的继承是基于原型的. 现在问题来了. 原型是什么?原型我们可以参照C++里的类,同样的保存了对象的属性和方法.例如我们写一个简单的对象 function Animal(name) {   

javascript原型链理解

javascript原型和原型链的引入,最初的目的是属性和方法的共享.没有原型,我们使用同一个构造函数新建的一系列对象,就都拥有一组完全相互独立的属性和方法,但是方法和一些属性我们不需要所有对象都各自有一个,有时候我们甚至需要所有对象的这些属性和方法是同一个,可以达到修改一个,所有的都要改变.这种需求在C#.java等语言中使用类和继承来实现,在js中没有用这种方法,而是给多有对象给了一个__proto__属性,同一个构造函数创建的所有对象的__proto__属性指向同一个对象,这个对象就是构造

Javascript原型继承容易忽略的错误

编写Javascript的开发者都知道,JS虽然没有类(ES6添加了class语法),但是可以模拟出OOP语言的类和面向对象的概念,比如我们都知道的一句话,Javascript中处处是对象,而面向对象语言的特性是继承,封装,多态,抽象,而本文讨论的是Javascript的继承,Javascript的继承方式有原型继承,组合继承,寄生继承等等,在日常开发中,哪种继承方式更好用在于开发者对于程序的结果以及性能的考虑.笔者在下面列举出原型继承中经常容易被忽略的错误. 常见错误一: function F

javascript原型继承圣杯模式

javascript纯面向对象开发需要使用到的一个模式,来对对象之间原型继承做中间层代理避免重复继承与代码杂乱 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"

通过原型继承理解ES6 extends 如何实现继承

前言 第一次接触到 ES6 中的 class 和 extends 时,就听人说这两个关键字不过是语法糖而已.它们的本质还是 ES3 的构造函数,原型链那些东西,没有什么新鲜的,只要理解了原型链等这些概念自然就明白了.这话说的没错,但是这些继承的实现是否是我们想的那样呢,今天让我们来用原型链解释下 ES6 extends 如何实现的继承. 结论 这里先上结论,如果有理解不对的地方,欢迎在留言指出:如果有不理解的地方可以看完结论后继续阅读,如果阅读完后有难以理解指出也欢迎留言讨论. extends

javascript --- 原型继承与属性拷贝的综合应用

对于继承来说,主要目标就是将一些现有的功能据为己有.也就是说,我们在新建一个对象的时候,通常首先继承现有对象,然后再为其添加额外的属性和方法. 对此,我们可以通过一个函数调用来完成. 具体而言就是: 1. 使用原型继承的方式,将一个已有对象设置成新对象的原型. 2. 新建一个对象后,将另一个已有对象的属性拷贝过来. function objectPlus(o, stuff){ var n; function F(){}; F.prototype = o.prototype; n = new F(