js高端系列教程(26)――JavaScript探秘:强大的.

2025-04-27

JavaScript 不包含传统的类继承模型,而是使用prototypal 原型模型。虽然这经常被当作是JavaScript 的缺点被提及,其实基于原型的继承模型比传统的类继承还要强大。实现传统的类继承模型是很简单,但是实现JavaScript 中的原型继承则要困难的多。

由于JavaScript 是唯一一个被广泛使用的基于原型继承的语言,所以理解两种继承模式的差异是需要一定时间的,今天我们就来了解一下原型和原型链。

原型

10年前,我刚学习JavaScript的时候,一般都是用如下方式来写代码: var decimalDigits = 2, tax = 5;

function add(x, y { return x + y; }

function subtract(x, y { return x - y; }

//alert(add(1, 3;

通过执行各个function来得到结果,学习了原型之后,我们可以使用如下方式来美化一下代码。

原型使用方式1

在使用原型之前,我们需要先将代码做一下小修改: var Calculator = function (decimalDigits, tax { this.decimalDigits = decimalDigits; this.tax = tax; };

然后,通过给Calculator对象的prototype属性赋值对象字面量来设定Calculator对象的原型。

Calculator.prototype = { add: function (x, y { return x + y; },

subtract: function (x, y { return x - y; } };

//alert((new Calculator(.add(1, 3;

这样,我们就可以new Calculator对象以后,就可以调用add方法来计算结果了。 原型使用方式2

第二种方式是,在赋值原型prototype的时候使用function立即执行的表达式来赋值,即如下格式:

Calculator.prototype =function( { } (;

它的好处在前面的帖子里已经知道了,就是可以封装私有的function,通过return的形式暴露出简单的使用名称,以达到public/private的效果,修改后的代码如下:

Calculator.prototype = function ( { add = function (x, y { return x + y; },

subtract = function (x, y { return x - y; } return { add: add, subtract: subtract } } (;

//alert((new Calculator(.add(11, 3;

同样的方式,我们可以new Calculator对象以后调用add方法来计算结果了。

分步声明

上述使用原型的时候,有一个限制就是一次性设置了原型对象,我们再来说一下如何分来设置原型的每个属性吧。

var BaseCalculator = function ( { //为每个实例都声明一个小数位数 this.decimalDigits = 2; };

//使用原型给BaseCalculator扩展2个对象方法 BaseCalculator.prototype.add = function (x, y { return x + y; };

BaseCalculator.prototype.subtract = function (x, y { return x - y; };

首先,声明了一个BaseCalculator对象,构造函数里会初始化一个小数位数的属性decimalDigits,然后通过原型属性设置2个function,分别是add(x,y和subtract(x,y,当然你也可以使用前面提到的2种方式的任何一种,我们的主要目的是看如何将BaseCalculator对象设置到真正的Calculator的原型上。

var BaseCalculator = function( { this.decimalDigits = 2;

};

BaseCalculator.prototype = { add: function(x, y { return x + y; },

subtract: function(x, y { return x - y; } };

创建完上述代码以后,我们来开始: var Calculator = function ( { //为每个实例都声明一个税收数字 this.tax = 5; };

Calculator.prototype = new BaseCalculator(;

我们可以看到Calculator的原型是指向到BaseCalculator的一个实例上,目的是让Calculator集成它的add(x,y和subtract(x,y这2个function,还有一点要说的是,由于它的原型是BaseCalculator的一个实例,所以不管你创建多少个Calculator对象实例,他们的原型指向的都是同一个实例。

var calc = new Calculator(;

alert(calc.add(1, 1;

//BaseCalculator 里声明的decimalDigits属性,在 Calculator里是可以访问到的 alert(calc.decimalDigits;

上面的代码,运行以后,我们可以看到因为Calculator的原型是指向

BaseCalculator的实例上的,所以可以访问他的decimalDigits属性值,那如果我不想让Calculator访问BaseCalculator的构造函数里声明的属性值,那怎么办呢?这么办:

var Calculator = function ( { this.tax= 5; };

Calculator.prototype = BaseCalculator.prototype;

通过将BaseCalculator的原型赋给Calculator的原型,这样你在Calculator的实例上就访问不到那个decimalDigits值了,如果你访问如下代码,那将会提升出错。

var calc = new Calculator(; alert(calc.add(1, 1; alert(calc.decimalDigits; 重写原型

在使用第三方JS类库的时候,往往有时候他们定义的原型方法是不能满足我们的需要,但是又离不开这个类库,所以这时候我们就需要重写他们的原型中的一个或者多个属性或function,我们可以通过继续声明的同样的add代码的形式来达到覆盖重写前面的add功能,代码如下:

//覆盖前面Calculator的add( function

Calculator.prototype.add = function (x, y { return x + y + this.tax; };

var calc = new Calculator(; alert(calc.add(1, 1;

这样,我们计算得出的结果就比原来多出了一个tax的值,但是有一点需要注意:那就是重写的代码需要放在最后,这样才能覆盖前面的代码。

原型链

在将原型链之前,我们先上一段代码: function Foo( { this.value = 42; }

Foo.prototype = { method: function( {} };

function Bar( {}

// 设置Bar的prototype属性为Foo的实例对象 Bar.prototype = new Foo(; Bar.prototype.foo = 'Hello World';

// 修正Bar.prototype.constructor为Bar本身 Bar.prototype.constructor = Bar;

var test = new Bar( // 创建Bar的一个新实例 // 原型链 test [Bar的实例]

Bar.prototype [Foo的实例] { foo: 'Hello World' } Foo.prototype {method: ...}; Object.prototype {toString: ... /* etc. */};

上面的例子中,test 对象从Bar.prototype 和Foo.prototype 继承下来;因此,它能访问Foo 的原型方法method。同时,它也能够访问那个定义在原型上的Foo 实例属性value。需要注意的是new Bar(

不会创造出一个新的Foo 实例,而是重复使用它原型上的那个实例; 因此,所有的Bar 实例都会共享相同的value 属性。 属性查找

当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找 到给定名称的属性为止,到查找到达原型链的顶部- 也就是

Object.prototype - 但是仍然没有找到指定的属性,就会返回undefined,我们来看一个例子:

function foo( {

this.add = function (x, y { return x + y; } }

foo.prototype.add = function (x, y {

return x + y + 10; } Object.prototype.subtract = function (x, y { return x - y; } var f = new foo(; alert(f.add(1, 2; //结果是 3,而不是 13 alert(f.subtract(1, 2; //结果是-1 通 过 代码 运 行 , 我 们 发现 subtract 是 安 装 我 们 所说 的 向 上 查 找 来 得 到 结 果的 , 但 是 add 方 式 有点 小 不 同 , 这 也 是 我想 强 调 的 , 就 是 属 性在 查 找 的时 候 是 先 查 找 自 身 的属 性 , 如 果 没 有 再 查找 原 型 , 再 没 有 , 再往 上 走 ,一 直 插 到 Object 的 原 型 上 ,所以 在 某 种 层面 上 说 ,用 for in 语 句 遍 历 属性 的 时 候 , 效 率 也 是个 问 题 。 还 有 一点 我 们 需 要 注 意 的 是, 我 们 可 以 赋 值 任 何类 型 的 对 象 到 原 型 上 , 但是 不 能 赋 值 原 子 类 型的 值 , 比 如 如 下 代 码是 无 效 的 : function Foo( {} Foo.prototype = 1; // 无效 hasOwnProperty 函数 hasOwnProperty 是 Object.prototype 的 一 个 方法 , 它可 是 个 好 东 西, 他 能 判断 一 个 对 象 是 否 包 含自 定 义 属 性 而 不 是 原型 链 上 的 属 性 , 因 为 hasOwnProperty 是 JavaScript 中 唯 一 一 个 处理 属 性 但 是 不 查 找 原型 链 的 函数 。 // 修改 Object.prototype Object.prototype.bar = 1; var foo = {goo: undefined}; foo.bar; // 1 'bar' in foo; // true foo.hasOwnProperty('bar'; // false

foo.hasOwnProperty('goo'; // true 只 有 hasOwnProperty 可 以 给 出 正确 和 期 望 的结 果 ,这 在遍 历 对 象 的 属 性时 会 很 有 用 。 没 有其 它 方 法 可 以 用 来 排除 原 型 链 上 的 属 性 ,而 不 是 定 义在 对 象 自 身 上 的 属 性。

但 有 个恶 心 的 地 方 是 : JavaScript 不 会保 护 hasOwnProperty 被 非 法 占 用, 因 此 如 果 一 个 对 象碰 巧 存 在 这 个 属 性 ,就 需 要 使 用 外 部 的 hasOwnProperty 函 数 来 获取 正 确 的 结 果 。 var foo = { hasOwnProperty: function( { return false; }, bar: 'Here be dragons' }; foo.hasOwnProperty('bar'; // 总是返回 false // 使用{}对象的 hasOwnProperty,并将其上下为设置为 foo

{}.hasOwnProperty.call(foo, 'bar'; // true 当 检 查 对 象 上 某 个 属 性 是 否 存 在 时 , hasOwnProperty 是 唯 一 可 用 的 方 法 。 同 时 在 使 用 for in loop 遍 历 对 象 时 , 推 荐 总 是 使 用 hasOwnProperty 方 法 , 这 将 会 避 免 原 型 对 象 扩 展 带 来 的 干 扰 , 我 们 来 看 一 下例 子 : // 修改 Object.prototype

Object.prototype.bar = 1; var foo = {moo: 2}; for(var i in foo { console.log(i; // 输出两个属性:bar 和 moo } 我 们 没 办 法 改 变 for in 语 句 的 行 为 , 所 以 想 过 滤 结 果 就 只 能 使 用 hasOwnProperty 方 法 , 代码 如 下 : // foo 变量是上例中的 for(var i in foo { if (foo.hasOwnProperty(i { console.log(i; } } 这 个 版本 的 代 码 是 唯 一 正 确的 写 法 。 由 于 我 们 使用 了 hasOwnProperty, 以 这 次只 输 出 moo。 果 不使 用 hasOwnProperty, 所 如 则 这 段代 码 在 原 生 对 象 原 型( 比 如 Object.prototype)被 扩 展 时 可 能会 出错。 总 结 :推 荐 使 用 hasOwnProperty, 不要 对 代 码运 行 的 环 境 做 任 何 假 设 ,不 要 假 设 原 生 对 象 是否 已 经 被 扩 展 了 。 总结

原 型 极大 地 丰 富 了 我 们 的 开发 代 码 , 但 是 在 平 时使 用 的 过 程 中 一 定 要 注 意上 述 提 到 的 一 些 注 意事 项 。


js高端系列教程(26)――JavaScript探秘:强大的..doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:启智阅读(1)

相关阅读
本类排行
× 游客快捷下载通道(下载后可以自由复制和排版)

下载本文档需要支付 7

支付方式:

开通VIP包月会员 特价:29元/月

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信:xuecool-com QQ:370150219