javascript 继承(转)

 

     javascript继承一直不好理解,每次遇到了看了似乎懂了,但是没有彻底研究过而且遇到一次忘记一次,这次想彻底的解决掉,用最简单直白的阐述。引入了前辈的一些看法,进行了收集加工整理。
用百度脑图做了整理

一,javascript中继承的由来

1-1.javascript的出现

     javascript出现是为了增强用户和浏览器之间的交互。比如,如果网页上有一栏"用户名"要求填写,浏览器就无法判断访问者是否真的填写了,只有让服务器端判断。如果没有填写,服务器端就返回错误,要求用户重新填写,这太浪费时间和服务器资源了。
当时javascript的作者Brendan Eich受到当时面向对象编程的影响,主要是Java的影响,Javascript里面所有的数据类型都是对象(object)。这时候,他要考虑一个问题,javascript到底需不需要继承?

1-2.Brendan Eich的选择

     javascript中是没有“类”这个概念的,new 后面不是面向对象编程中的“类”,而是构造函数。如例:

function DOG(name){
    this.name = name;
}
//对这个构造函数使用new,就会生成一个狗对象的实例。var dogA = new DOG('大毛');
alert(dogA.name); // 大毛

1-3.new运算符的缺点

     用构造函数生成实例对象,有一个缺点,那就是无法共享属性和方法。

function DOG(name){
    this.name = name;
  this.species = '犬科';
}
//生成两个实例对象:var dogA = new DOG('大毛');
var dogB = new DOG('二毛');

dogA.species = '猫科';
alert(dogB.species); // 显示"犬科",不受dogA的影响

     每一个实例对象,都有自己的属性和方法的副本。这不仅无法做到数据共享,也是极大的资源浪费。

1-4.prototype属性的引入

     考虑到这一点,Brendan Eich决定为构造函数设置一个prototype属性。

     这个属性包含一个对象(以下简称"prototype对象"),所有实例对象需要共享的属性和方法,都放在这个对象里面;那些不需要共享的属性和方法,就放在构造函数里面。

     实例对象一旦创建,将自动引用prototype对象的属性和方法。也就是说,实例对象的属性和方法,分成两种,一种是本地的,另一种是引用的。

如例:

function DOG(name){
    this.name = name;
}
DOG.prototype = { species : '犬科' };

var dogA = new DOG('大毛');
var dogB = new DOG('二毛');

alert(dogA.species); // 犬科
alert(dogB.species); // 犬科//只要修改了prototype对象,就会同时影响到两个实例对象。
DOG.prototype.species = '猫科';
alert(dogA.species); // 猫科
alert(dogB.species); // 猫科

1-5 总结

     由于所有的实例对象共享同一个prototype对象,那么从外界看起来,prototype对象就好像是实例对象的原型,而实例对象则好像"继承"了prototype对象一样。

关于第一章节为阮一峰《Javascript继承机制的设计思想》中观点。

二,构造函数

     构造函数 就是一个普通的函数,通常当函数名 为 大写开头的,我们认为是构造函数,否则 就是普通的方法。

// 构造函数function A() {
    this.name = 'A Class instance';
}
// 普通方法function m1() {
}

三.Null是原型链的最后一环

JavaScript只有一种结构:对象。每个对象都有一个内部链接指向另一个对象,这个对象称为原型 (prototype)。那个原型对象也有自己的原型,如此直到某个对象以null作为其原型。null,根据定义,没有原型,作为这种原型链的最后一环而存在。

     尝试用语言去解释

// 假设有个对象o,其原型链如下所示:// var o = {a: 1, b: 2} ---> {b: 3, c: 4} ---> null// 'a'和'b'是o的自有属性。

// 本例中,someObject.[[Prototype]]指定someObject的原型。// 这完全是一种标记符号(基于ECMAScript标准中所使用的),不可用于脚本中。

console.log(o.a);   // 1// o有一个自有属性'a'吗?是的,其值为1

console.log(o.b);   // 2// o有自有属性'b'吗?是的,其值为2// o的原型也有一个属性'b',但是这里不会被访问。这被称为“属性隐藏”(property shadowing)

console.log(o.c);   // 4// o有自有属性'c'吗?没有,检查它的原型// o.[[Prototype]]有自有属性'c'吗?是的,其值为4。

console.log(o.d);   // undefined// o有自有属性'd'吗?没有,检查其原型// o.[[Prototype]]有自有属性'd'吗?没有,检查其原型// o.[[Prototype]].[[Prototype]]为null,停止搜索,没有找到属性,返回undefined。

我们用代码去解释

var o = {a: 1};

// 新创建的对象o有Object.prototype作为其[[Prototype]]// o没有名为'hasOwnProperty'的自有属性// hasOwnProperty是Object.prototype的自有属性。因此o从Object.prototype继承了hasOwnProperty// Object.prototype以null为其prototype。// o ---> Object.prototype ---> null

var a = ["yo", "whadup", "?"];

// 数组继承自Array.prototype(它具有indexOf, forEach等方法)。// 该原型链如下所示:// a ---> Array.prototype ---> Object.prototype ---> null

function f() {
    return 2;
}

// 函数继承自Function.prototype(它具有call,bind等方法):// f ---> Function.prototype ---> Object.prototype ---> null

四,参考的资料和笔记:

posted @ 2015-04-30 17:41  慕容晓峰  阅读(164)  评论(0编辑  收藏  举报