脚本宝典收集整理的这篇文章主要介绍了javascript代码实例教程-Javascript ֮ Prototype,脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。小宝典致力于为广大程序猿(媛)提供高品质的代码服务,请大家多多光顾小站,小宝典在此谢过。
JavaScript 中的 function
本质就是一个 object
对象,它本身包含了一些方法(apply(),call())和一些属性(length
, constructor
),这其中还包含一个名为 prototype
的属性。
当你定义了一个 function 后,你就能访问到这个 prototype
属性,它的初始值是一个”空”的 object
对象:
function foo() { ... } typeof foo.prototype; // "object"
你可以随意设定这个对象,给它加上属性或者方法,但是这不会对这个 function
本身造成任何影响,除非你把它最为构造函数来使用。
当使用 new
来实例化一个对象时,在 function
内可以通过 this
关键字来对这个对象进行成员追加:
function Gadget(name, color) { this.name = name; this.color = color; this.whatAreYou = function () { return 'I am a ' + this.color + ' ' + this.name; }; }
另外我们也可以在 function
的 prototype
属性上进行相同的处理:
Gadget.prototype.price = 100; Gadget.prototype.rating = 3; Gadget.prototype.getInfo = function () { return 'Rating: ' + this.rating + ', price: ' + this.price; };
prototype
对象甚至可以被整个替换:
Gadget.prototype = { price: 100, rating: ... /* 其他成员... */ };
prototype
上添加的方法和属性都能够在实例化后的对象上调用:
VAR newtoy = new Gadget('webcam', 'black'); newtoy.name; // "webcam" newtoy.color; // "black" newtoy.whatAreYou(); // "I am a black webcam" newtoy.price; // 100 newtoy.rating; // 3 newtoy.getInfo(); // "Rating: 3, price: 100"
object
对象在 Javascript 中都是以引用方式传递的,所以 prototype
并非在每个实例对象中保存一份。当你改变 prototype
时,所有的实例对象都能立即“察觉”这些变动。假设我们再增加一个新的方法:
Gadget.prototype.get = function (what) { return this[what]; };
前文的 newtoy 虽然在此前已经被实例化,但他仍然能使用到这个新方法:
newtoy.get('price'); // 100 newtoy.get('color'); // "black"
先前我们定义过一个 getInfo() 方法,它修改为以下的方式后也能获得同样的输出结果:
Gadget.prototype.getInfo = function () { return 'Rating: ' + Gadget.prototype.rating + ', price: ' + Gadget.prototype.price; };
这个原由需要从头说起,先来看 newtoy 对象是怎样实例化的:
var newtoy = new Gadget('webcam', 'black');
当你访问 newtoy 的某个属性的时候(这里假设是 newtoy.name),JavaScript 引擎会搜索它的所有名为 name 的属性,如果发现了就返回它的值:
newtoy.name; // "webcam"
当你访问 rating 属性时情况变了,JavaScript 引擎在 newtoy 上找不到名为 rating 的属性,然后他就会到 newtoy 的构造函数(Gadget) 的 prototype
属性上继续查找:
newtoy.rating; // 3
以下的代码验证了这一点:
newtoy.constructor === Gadget; // true newtoy.constructor.prototype.rating; // 3
我们知道每个 object 对象都有一个构造函数,那么作为 object
对象的 prototype
也必然存在一个构造函数。这就形成了一个 prototype chain (prototype 链),这个链的最上层就是内置的 Object() 对象。 要验证这一点很容易,newtoy 没有 toString() 方法,它的 prototype
上也没有,但是你却能调用 newtoy.toString() ,因为 object
对象有这个方法:
newtoy.toString(); // "[object Object]"
当自有属性与 prototype
属性重名时,自有属性优先:
function Gadget(name) { this.name = name; } Gadget.prototype.name = 'mirror'; var toy = new Gadget('camera'); toy.name; // "camera"
使用 hasOwnProperty() 可以知道某个属性知否是自有属性:
toy.hasOwnProperty('name'); // true
我们把自有属性 name 删了再瞧瞧什么情况:
delete toy.name; // true toy.name; // "mirror" toy.hasOwnProperty('name'); // false
使用 for-in 语句能够遍历出一个对象的所有属性:
var params = { productid: 666, section: 'products' }; var url = 'https://example.org/page.php?', i, query = []; for (i in params) { query.push(i + '=' + params[i]); } url += query.join('&');
以上代码输出:
https://example.org/page.php?productid=666§ion=products
以下几点需要注意:
并非所有属性能够在 for-in 中遍历到,譬如constructor
属性等等。所有能遍历到的属性称为 enumerable。你可以用 propertyIsEnumerable() 方法来区分,在 ES5 中你甚至可以自定义哪些属性是 enumerable。 Prototype 链上的 enumerable 属性也会被遍历到。 由 Prototype 链上而来的 enumerable 属性,被传入 propertyIsEnumerable() 方法时总返回 false
。 通过实例来看一下:
function Gadget(name, color) { this.name = name; this.color = color; this.getName = function () { return this.name; }; } Gadget.prototype.price = 100; Gadget.prototype.rating = 3; var newtoy = new Gadget('webcam', 'black'); for (var prop in newtoy) { console.LOG(prop + ' = ' + newtoy[prop]); } // 输出 name = webcam color = black getName = function () { return this.name; } price = 100 rating = 3
再试一下 propertyIsEnumerable() 方法:
// 自有属性 newtoy.propertyIsEnumerable('name'); // true // 内置属性 newtoy.propertyIsEnumerable('constructor'); // false // prototype 链上的属性 newtoy.propertyIsEnumerable('price'); // false // 改变调用对象后 newtoy.constructor.prototype.propertyIsEnumerable('price'); // true
isPrototypeOf() 可以用来确认某个对象是否另一个对象的 prototype
。
var monkey = { hair: true, feeds: 'bananas', breathes: 'air' }; function Human(name) { this.name = name; } Human.prototype = monkey; var george = new Human('George'); monkey.isPrototypeOf(george); // true
那么当你对 prototype
一无所知时,怎么办呢?对于支持 ES5 的环境,你可以使用 getPrototypeOf() 方法。
> Object.getPrototypeOf(george).feeds; "bananas" > Object.getPrototypeOf(george) === monkey; true
如果遇到不支持 ES5 的环境,你可以使用 __proto__ 这个特殊的属性。
前文提到当你访问一个非自有属性时, 引擎通过 prototype
继续查找:
var monkey = { feeds: 'bananas', breathes: 'air' }; function Human() {} Human.prototype = monkey; var developer = new Human(); developer.feeds = 'pizza'; developer.hacks = 'JavaScript'; developer.feeds; // "pizza" developer.breathes; // "air"
在现今许多 JavaScript 环境中,都是通过一个名为 __proto__ 的属性来实现的:
developer.__proto__ === monkey; // true
需要注意的是 __proto__ 与 prototype 是不同的,__proto__ 是一个实例对象的属性,而 prototype
是一个构造函数的属性。
typeof developer.__proto__; // "object" typeof developer.prototype; // "undefined" typeof developer.constructor.prototype; // "object"
你应当仅在调试环境中使用这个 __proto__ 属性来获取信息
内置的构造函数(诸如:Array,String 以及 Object)都可以通过 prototype
来进行扩展。见示例:
Array.prototype.inArray = function (needle) { for (var i = 0, len = this.length; i < len; i++) { if (this[i] === needle) { return true; } } return false; }; var colors = ['red', 'green', 'blue']; colors.inArray('red'); // true colors.inArray('yellow'); // false
上面的方法与 apply() 等函数的灵活结合可以写出非常高效的代码。譬如我们为 String 增加一个字符串反转的函数:
String.prototype.reverse = function () { return Array.prototype.reverse.apply(this.splIT('')).join(''); };
代码是不是简洁得出乎你的意料?
扩展内置对象是一个强大的功能,但不应当过度使用,因为对这些内置对象的修改会对使用者和维护者造成困惑。
另外随着各个浏览器的升级,JavaScript 环境也会对这些内置对象进行扩展,这就可能与你的扩展造成冲突。
目前有一些类库致力于在不同的 JavaScript 环境中提供一致的调用接口,这些类库被称之为 shims 或 polyfills。
自行对内置对象的扩展应当谨慎,以下提供一种比较保险的做法:
if (typeof String.prototype.trim !== 'function') { String.prototype.trim = function () { return this.replace(/^/s+|/s+$/g,''); }; } " hello ".trim(); // "hello"
当你操作 prototype
时应当牢记以下两点:
prototype
对象,那么你也打断了 prototype 链 prototype.constructor 是不可靠的 非常抽象难懂对不对,看个例子你就明白了:
function Dog() { this.tail = true; } var benji = new Dog();
此时如果你扩展 Dog(),prototype 链能保证已经实例化的 benji 能够使用到新的扩展:
Dog.prototype.say = function () { return 'Woof!'; }; benji.say(); // "Woof!" benji.constructor === Dog; // true
接着我们整个替换掉 Dog() 的 prototype 会怎样呢?
Dog.prototype = { paws: 4, hair: true }; typeof benji.paws; // "undefined" benji.say(); // "Woof!" typeof benji.__proto__.paws; // "undefined" typeof benji.__proto__.say; // "function"
早先实例化的 benji 对象无法访问到扩展成员(paws, hair),但它仍然能调用替换前的成员。
那么对于新的实例对象是什么情况呢?
var lucy = new Dog(); lucy.say(); // TypeError: lucy.say is not a function lucy.paws; // 4 typeof lucy.__proto__.say; // "undefined" typeof lucy.__proto__.paws; // "number"
这个新的实例对象显然可以调用到最新的扩展方法,如果你检查一下 constructor
属性你会发现返回值为 Object(),而不是预期的 Dog()。
lucy.constructor; // function Object() { [native code] } benji.constructor; // function Dog() { // this.tail = true; // }
你可以在整个替换 prototype
属性后强行改正 constructor
属性来避免发生这样的困扰。
function Dog() { ... } Dog.prototype = { ... }; new Dog().constructor === Dog; // false // 强行改正 constructor Dog.prototype.constructor = Dog; new Dog().constructor === Dog; // true
强烈建议每次整个替换
prototype
属性后强行设置一下constructor
属性。
function
都有一个名为 prototype
的属性,初始化时他只是一个空对象。 你可以在这个 prototype
对象上增加属性或方法,也可以整个替换掉它。 当你使用一个 function
来实例化一个对象时,这个对象会保存一个指向 function
的 prototype
属性的链接。 对象的自有属性优先于 prototype
属性。 hasOwnProperty() 可以用来区分自有属性和 prototype
属性。 JavaScript 的查找本质是在 prototype 链上查找。 内置的构造函数可以扩展,但扩展是需要注意不同的 JavaScript 环境,并在扩展前做好确认检查。 觉得可用,就经常来吧! 脚本宝典 欢迎评论哦! js脚本,巧夺天工,精雕玉琢。小宝典献丑了!
以上是脚本宝典为你收集整理的javascript代码实例教程-Javascript ֮ Prototype全部内容,希望文章能够帮你解决javascript代码实例教程-Javascript ֮ Prototype所遇到的问题。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。