js中的原型
一:原型属性
函数本身也是一个包含了方法和属性的对象。
定义一个函数foo(),访问其他对象一样访问该函数的属性:
function foo(a, b) {
return a * b;
}
foo.length
--2
foo.constructor
ƒ Function() { [native code] }
函数定义时被创建的属性就包括有prototype属性,他的初始值是一个“空”对象
typeof foo.prototype
"object"
可以自己添加该属性
foo.prototype={};
1.利用原型添加方法和属性
构建一个具体的构造函数Gadget(),在新建时为其添加属性和方法。
function Gadget(name, color) {
this.name = name;
this.color = color;
this.whatAreYou = function () {
return 'I am a ' + this.color + '' + this.name;
};
}
通过构造器函数的prototype属性来增加该构造器所能提供的功能
Gadget.prototype.price = 100;
Gadget.prototype.rating = 3;
Gadget.prototype.getInfo = function () {
return 'Rating:' + this.rating + ',price: ' + this.price;
}
也可以另外定义一个对象,将其覆盖到之前的原型上:
Gadget.prototype = {
price: 100,
rating:
}
2.使用原型的方法和属性:
直接用该构造器来新建对象,就可以访问之前定义的那些属性和方法。
var newtoy=new Gadget('webcam','black');
newtoy.name;
"webcam"
newtoy.color;
"black"
newtoy.whatAreYou();
"I am a blackwebcam"
newtoy.price;
100
newtoy.rating;
3
newtoy.getInfo();
"Rating:3,price: 100"
可以随时修改prototype属性,并且由同一构造器创建的所有对象的prototype属性也同时改变。
向原型中添加一个新方法:
Gadget.prototype.get = function (what) {
return this[what];
};
newtoy对象在get()方法定义之前就已经被创建,我们依然可以在该对象中访问新增的方法:
newtoy.get('color');
"black"
newtoy.get('price');
100
3.自身属性与原型属性
在getInfo()的示例中,我们使用this指针来完成对象的访问,但其实直接引用Gadget.prototype也可以完成同样的操作:
Gadget.prototype.getInfo = function () {
return 'Rating: ' + Gadget.prototype.rating +
',price: ' + Gadget.prototype.price;
};
回到之前的那个newtoy对象上:
var newtoy = new Gadget('webcam', 'black');
newtoy.name
"webcam"
newtoy.rating
3
每个对象都有属于自己的构造器属性,所引用的就是用于创建该对象的那个函数
newtoy.constructor===Gadget
true
newtoy.constructor.prototype.rating;
3
每个对象都会有一个构造器,而原型本身也是一个对象,这意味着它也必然有一个构造器,而这个构造器又会有自己的原型,这种结构可能会一直不断的持续下去,并最终取决于原型链的长度,但最后一环肯定是Object内建对象,它是最高级的父级对象。
4.利用自身属性重写原型属性
如果一个对象自身属性中没有找到指定的属性,就会使用原型链中查找到的相关属性,如果,对象的自身属性与原型属性同名,自身属性的优先级高于原型属性。
示例:同一个属性名同时出现在对象的自身属性和原型属性中:
function Gadget(name) {
this.name = name;
}
Gadget.prototype.name = 'sunyajing';
新建一个对象,并访问该对象自身的name属性
var toy = new Gadget('camera');
toy.name;
"camera"
通过hasOwnProperty()判断一个属性是自身属性还是原型属性
toy.hasOwnProperty('name');
true
删除这个属性,同名的原型属性就会“浮出水面”
delete toy.name;
true
toy.name;
"sunyajing"
toy.hasOwnProperty('name');
false
我们随时可以重建这个对象的自身属性:
toy.name = 'camera';
toy.name;
"camera"
判读一个对象的某个原型属性到底是原型链中哪个原型的属性呢?可以用hasOwnProperty()属性:
toy.toString();
"[object Object]"
toy.hasOwnProperty('toString');
false
toy.constructor.hasOwnProperty('toString');
false
toy.constructor.prototype.hasOwnProperty('toString');
false
Object.hasOwnProperty('toString');
false
Object.prototype.hasOwnProperty('toString');
true
for 更适合数组,for-in更适合对象,构造URL字符串为例:
var params = {
productid: 666,
section: 'products'
};
var url = 'http://example.org/page.php',
i,
query = [];
for (i in params) {
query.push(i + '=' + params[i]);
}
url += query.join('&');
"http://example.org/page.phpproductid=666§ion=products"
并不是所有的属性都会在for-in循环中显示,例如(数组的)length属性和constructor属性就不会被显示
1.会显示的属性被称为可枚举的,可以通过propertyIsEnumerable()方法来判断对象的某个属性是否可枚举。
2.原型链中的各个原型属性也会被显示出来,当然前提是可枚举的,可以通过对象的hasOwnProperty()方法来判断一个属性是对象自身属性还是原型属性。
3.对于所有的原型属性propertyIsEnumerable()都会返回false,包括那些在for-in循环中可枚举的属性
通过以下代码看一下具体的实现方法:
function Gadget(name, color) {
this.name = name;
this.color = color;
this.getName = function () {
return this.name;
};
}
Gadget.prototype.price = 100;
Gadget.prototype.rathing = 3;
对他执行for-in循环,就会列出该对象中的所有属性,包括原型中的属性:
for (var prop in newtoy) {
console.log(prop + ' = ' + newtoy[prop]);
}
结果:
name = webcam
color = black
getName = function () {
return this.name;
}
price = 100
rathing = 3
如果要对对象属性和原型属性做一个区分,就要调用hasOwnProperty()方法:
newtoy.hasOwnProperty('name');
true
newtoy.hasOwnProperty('price');
false
显示对象的自身属性:
for (var prop in newtoy) {
if (newtoy.hasOwnProperty(prop)) {
console.log(prop + ' = ' + newtoy[prop]);
}
}
结果:
name = webcam
color = black
getName = function () {
return this.name;
}
propertyIsEnumerable()会对所有的非内建对象属性返回true
newtoy.propertyIsEnumerable('name');
true
对于内建属性和方法大部分是不可枚举的
newtoy.propertyIsEnumerable('constructor');
false
如果propertyIsEnumerable()的调用是来自原型链上的某个对象,那么该对象中的属性是可枚举的
newtoy.constructor.prototype.propertyIsEnumerable('price');
true
5.isPrototypeOf()方法
每个对象中都会有一个isPrototypeOf()方法,这个方法告诉我们当前对象是否是另一个对象的原型
var monkey = {
hair: true,
feeds: 'bananas',
breathes: 'air'
};
创建一个Human的构造器函数,并设置原型属性指向monkey:
Human.prototype = monkey;
Human.prototype = monkey;
var george = new Human('George');
monkey.isPrototypeOf(george);
不知道某个原型的情况下,获得对象的原型。
Object.getPrototypeOf(george).feeds;
Object.getPrototypeOf(george) === monkey;
6.神秘的_proto_链接
改写用monkey对象做原型的Human()对象构造器。
var monkey = {
feeds: 'bananas',
breathes: 'air'
};
function Human() { }
Human.prototype = monkey;
创建一个developer对象,并赋予它一些属性:
var developer = new Human();
developer.feeds = 'pizza';
developer.hacks = 'JavaScript';
访问:
developer.hacks;
-- "JavaScript"
developer.feeds;
-- "pizza"
但是breathes在developer对象自身的属性中是不存在的,所以就得去原型中查找
developer.breathes;
"air"
这个神秘的链接叫做__proto__属性
developer.__proto__ === monkey;
true
__proto__是某个实例对象的属性,而prototype则是属于构造器函数的属性。
typeof developer.__proto__
"object"
typeof developer.prototype;
"undefined"
typeof developer.constructor.prototype;
"object"
二:扩展内建对象
内建对象的构造器函数(Array、String、Object、Function)都是可以通过原型来扩展的
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
反转字符串的功能:
String.prototype.reverse = function () {
return Array.prototype.reverse.apply(this.split('')).join('');
}
"sunyujing".reverse();
"gnijuynus"
为String对象添加trim()方法
if(typeof String.prototype.trim !== 'function'){
String.prototype.trim = function(){
return this.replace(/^\s+|\s+&/g,'');
};
} " hwllow ".trim();
"hwllow"
如果想要通过原型为某个对象添加一个新属性,务必检查一下该属性是否已经存在
三:原型陷阱
处理原型时,特别注意以下两种行为:
1.我们对原型对象执行完全替换时,可能会触发原型链中某种异常(exception)
2.prototype.constructor属性不可靠
新建一个简答的构造器函数,并创建两个对象
function Dog() {
this.tail = true;
}
var benji = new Dog();
var rusty = new Dog();
添加一个Say()方法:
Dog.prototype.say = function () {
return 'Woof';
};
上面的两个对象都可以访问该新方法。
benji.say();
"Woof"
rusty.say();
"Woof"
检查一下这些对象构造器函数,会发现一切正常。
benji.constructor === Dog;
--true
rusty.constructor === Dog;
--true
用一个自定义的新对象完全覆盖掉原有的原型对象:
Dog.prototype = {
paws: 4,
hair: true
};
这会使原有对象不能访问原型的新增属性,依然通过那个神秘的链接与原有的原型对象保持联系。
typeof benji.paws;
--"undefined"
benji.say();
--"Woof"
typeof benji.__proto__.say;
--"function"
typeof benji.__proto__paws;
--"undefined"
之后创建的所有对象使用的都是被更新后的prototype对象
var lucy = new Dog();
lucy.say();
Uncaught TypeError: lucy.say is not a function
lucy.paws;
--4
其神秘的链接__proto__也指向了新的prototype对象
typeof lucy.__proto__.paws;
---"number"
typeof lucy.__proto__.say;
---"undefined"
新对象的constructor属性就不能再保持正确了,原本是Dog()的引用却指向了Object()
lucy.constructor;
--ƒ Object() { [native code] }
benji.constructor;
--ƒ Dog() {
this.tail = true;
}
也可以通过重新设置constructor属性来解决上述所有的异常行为:
function Dog() { }
Dog.prototype = {};
new dog().constructor === Dog;
--false
Dog.prototype.constructor = Dog;
new Dog().constructor === Dog;
true
当我们重写某个对象的prototype时,需要重新设置相应的constructor属性。
js中的原型的更多相关文章
- js中的原型、继承的一些想法
最近看到一个别人写的js类库,突然对js中的原型及继承产生了一些想法,之前也看过其中的一些内容,但是总不是很清晰,这几天利用空闲时间,对这块理解了一下,感觉还是有不通之处,思路上没那么条理,仅作为分享 ...
- 谈谈JS中的原型
不知道大家对JS中的原型理解的怎么样,我想如果大家对JS中的原型对象以及prototype属性十分熟悉的话对后面原型链以及继承的理解会十分的容易,这里想和大家分享自己对其的理解,请先看下面这段代码O( ...
- js中的原型prototype
var arr1 = new Array(12,34,98,43,38,79,56,1); arr1.sum=function (){ var result = 0; for(var i=0; i&l ...
- JS中的原型继承机制
转载 http://blog.csdn.net/niuyongjie/article/details/4810835 在学习JS的面向对象过程中,一直对constructor与prototype感到很 ...
- js中的原型以及原型链
在js中原型是每个构造函数的属性: 这个算 js 核心概念的一部分 var f1 = new Foo(); 对象 f1 的构造函数就是 Foo , f1的原型 __proto__ 就指向构造函数 Fo ...
- js中的原型哲学思想
https://segmentfault.com/a/1190000005824449 记得当年初试前端的时候,学习JavaScript过程中,原型问题一直让我疑惑许久,那时候捧着那本著名的红皮书,看 ...
- JS中的原型对象与构造器
在Javascript中:原型对象是属于构造函数的,不属于实例:实例只能共享原型对象中的属性和方法(当然也可以有自己的属性和方法,或者覆盖原型中同名的属性和方法):构造器constructor属于原型 ...
- 理解js中的原型链
对象有”prototype”属性,函数对象有”prototype”属性,原型对象有”constructor”属性. 关于原型 在JavaScript中,原型也是一个对象,通过原型可以实现对象的属性继承 ...
- JS中注意原型链的“指向”
昨天压缩Js文件时发现了项目中的一个prototype的问题代码如下所示: 1. <script> var XXX = function(){ }; var x1 = new XXX(); ...
随机推荐
- [转] Torch中实现mini-batch RNN
工作中需要把一个SGD的LSTM改造成mini-batch的LSTM, 两篇比较有用的博文,转载mark https://zhuanlan.zhihu.com/p/34418001 http://ww ...
- Python 面向对象【1】
对象 = 属性 + 方法 面向对象特征:分装 继承 多态[不同对象对同一方法响应不同行动] 类定义 class xxx: .... .... 类对象 类对象支持两种操作:属性引用和实例化 ...
- leetcode 307 Range Sum Query
问题描述:给定一序列,求任意区间(i, j)的元素和:修改任意一元素,实现快速更新 树状数组 树状数组的主要特点是生成一棵树,树的高度为logN.每一层的高度为k,分布在这一层的序列元素索引的二进制表 ...
- 【转】C++中substr的用法
substr有2种用法:假设:string s = "0123456789"; string sub1 = s.substr(5); //只有一个数字5表示从下标为5开始一直到结尾 ...
- Unity3D Shader 入门
什么是Shader Shader(着色器)是一段能够针对3D对象进行操作.并被GPU所执行的程序,它负责将输入的Mesh(网格)以指定的方式和输入的贴图或者颜色等组合作用,然后输出.绘图单元可以依据这 ...
- C语言 16进制转float
float hex_to_float(uint8_t *data) { float num = 0.0; uint8_t dd[4] = {data[0], data[1], data[2], dat ...
- dubbo @Activate 注解使用和实现解析
Activate注解表示一个扩展是否被激活(使用),可以放在类定义和方法上, dubbo用它在spi扩展类定义上,表示这个扩展实现激活条件和时机. 先看下定义: @Documented @Retent ...
- tornado的异步效果
第一种方式: import tornado.ioloop import tornado.web from tornado import gen from tornado.concurrent impo ...
- django的内置信号
Model singnalspre_init 在model执行构造方法之前自动触发post_init django的model在执行构造方法之后,自动触发pre_save django的对象保存之前, ...
- 18)django-模板的过滤器和tag,自定义simple_tag和filter
模板过滤器是在变量被显示前修改它的值的一个简单方法. 过滤器使用管道字符 . 模板标签(template tag) .标签(tag)定义比较明确,即: 仅通知模板系统完成某些工作的标签. 一:dja ...