您的位置:首页 > Web前端 > JavaScript

javascript原型理解

2017-01-22 10:59 99 查看
    想来每一个前端开发者都会对JavaScript原型有过一定的研究,对于JavaScript的学习我曾经有过许多疑惑,但是后来在慢慢的学习过程中很多问题得到了理解,不过仍然有些问题估计还是需要不断在实践中得到锻炼和学习吧,今天写这篇文章的目的是记录下我对JavaScript的原型理解

什么是原型

原型是一个对象,其他对象可以通过它实现属性继承。任何对象都有一个原型,但是原型本身也是对象,所以每个原型本身又会有一个原型,这种关系持续直到原型已经到了尽头,即在原型链的顶端。

一个对象的真正原型是被对象内部的[[Prototype]]属性(property)所持有,ECMA引入了标准对象原型访问器Object.getProperty(object),或者使用除IE浏览器其他都支持非标准的访问器__proto__

var a = {};

//Firefox 3.6 and Chrome 5

Object.getPrototypeOf(a); //[object Object]

//Firefox 3.6, Chrome 5 and Safari 4

a.__proto__; //[object Object]

//all browsers

a.constructor.prototype; //[object Object]
首先解释下prototype和__proto__的概念

prototype是函数的一个属性(每个函数都有一个prototype属性),这个属性是一个指针,指向一个对象,它是显示修改对象的原型的属性。

__proto__是一个对象拥有的内置属性(请注意:prototyoe是函数的内置属性,__proto__是对象的内置属性),是JS内部使用寻找原型链的属性

举例说明:

var Car=function(){};

var littlercar=new Car();

new过程被拆分为三步:

1)var littlecar={}初始化一个对象littlecar

2)littlecar.__proto__==Car.prototype;

3)Car.call(littilecar)也就是说构造littlecar,即初始化littlecar

var Person = function(){};
Person.prototype.sayName = function() {
alert("My Name is Jacky");
};

Person.prototype.age = 27;
var p = new Person();
p.sayName();
p是一个引用指向Person的对象,我们在Person的原型上定义了sayName方法和age属性,当我们执行p.age时候,会先在this的内部查找(也就是构造函数内部),如果没有找到然后再沿着原型链向上追溯,这里的向上追溯是怎么向上的呢?这里就是使用__proto__属性链接到原型(也就是Person.prototpe)进行查找,最终在原型上找到了age属性
原型的使用通常在继承中比较多,如果我在继承中使用原型,应该怎么做?如果说我们现在有个实例,想让这个实例实现某个对象的功能,比如说String,我们可以这样写:

var a = {};
a.__proto__ = String.prototype;
console.log(a.length);
console.log('&&&');
打印结果:



在这个例子中,首先创建了一个对象a,然后通过a的原型来继承String这个对象的功能。

但是这个例子通常用的很少,更多的情况是我们首先创建一个函数,然后定义这个函数的原型对象,往原型对象中添加属性,然后我们new 生成实例,这样每个实例都可以继承原型的属性。

从别处看到了一句话:(构造函数存在的原因:构造函数提供了一种方便的跨浏览器机制,这种机制允许在创建实例时为实例提供一个通用的原型)JavaScript并没有区分构造函数和普通函数一个函数的原型属性(portotype动态方法
  property静态属性)是一个对象,当这个函数被用来做构造函数来创建实例时候,该函数的原型属性将被作为原型赋值给生成的对象实例,即所有实例的原型引用都是函数的原型属性

下面从别处拷贝了段代码

//创建一个函数father
var father = function(){ this.sex='mail' ;}
//使用father 创建一个对象实例son
var son = new father ();
//查看father 和son的构造函数
father .constructor;  // function Function() { [native code]}
father .constructor==Function.constructor; //true
son.constructor; //实例c的构造函数 即 b function(){ var one; }
son.constructor==father  //true
//father 是一个函数,查看father 的原型如下
father .constructor.prototype // function (){}
father .__proto__  //function (){}

//father 是一个函数,由于javascript没有在构造函数constructor和函数function之间做区分,所以函数像constructor一样,
//有一个原型属性,这和函数的原型(father .__proto__ 或者father .construtor.prototype)是不一样的
father .prototype //[object Object]   函数b的原型属性

father .prototype==father .constructor.prototype //fasle
father .prototype==father .__proto__  //false
father .__proto__==father .constructor.prototype //true

//son是一个由father 创建的对象实例,查看son的原型如下
son.constructor.prototype //[object Object] 这是对象的原型
son.__proto__ //[object Object] 这是对象的原型

son.constructor.prototype==father.constructor.prototype;  //false  son的原型和father 的原型比较
son.constructor.prototype==father.prototype;  //true son的原型和father的原型属性比较

//为函数father的原型属性添加一个属性max
father .prototype.max = 3
//实例c也有了一个属性max
son.max  //3

上面的例子中,对象实例son的原型和函数的father的原型属性是一样的,如果改变father的原型属性,则对象实例son的原型也会改变

理解一个函数的原型属性(function’s prototype property )其实和实际的原型(prototype)没有关系对我们来说至关重要

//(example fails in IE)

var A = function(name) {

this.name = name;

}

A.prototype == A.__proto__; //false

A.__proto__ == Function.prototype; //true - A's prototype is set to its constructor's prototype property

给个例子你可能曾经上百次的像这样使用javascript,现在当你再次看到这样的代码的时候,你或许会有不同的理解。

//Constructor. <em>this</em> is returned as new object and its internal [[prototype]] property will be set to the constructor's default prototype property
var Circle = function(radius) {
this.radius = radius;
//this.__proto__ = Circle.prototype;
}

Circle.prototype.area = function() {
return Math.PI*this.radius*this.radius;
}
//create two instances of a circle and make each leverage the common prototype
var a = new Circle(3), b = new Circle(4);
a.area().toFixed(2); //28.27
b.area().toFixed(2); //50.27

棒极了。如果我更改了构造函数的原型,是否意味着已经存在的该构造函数的实例将获得构造函数的最新版本?

如果修改的是原型属性,那么这样的改变将会发生。因为在a实际被创建之后,a.__proto__是一个对A.prototype 的一个引用。

var A = function(name) {
this.name = name;
}
var a = new A('alpha');
a.name; //'alpha'
A.prototype.x = 23;
a.x; //23
在上面这个例子中,修改了A的原型属性,那么a的原型也发生了变化,因为实例对象a的原型(a.__proto__)是对函数A的原型属性(A.prototype)的引用, 但是对函数A的原型进行了修改,但是并没有反应到A所创建的实例a中
var A = function(name)
{
this.name = name;
}
var a = new A(‘alpha’);
a.name; //’alpha’

A.__proto__.max = 19880716;

a.max   //undefined

但是如果我现在替换A的原型属性为一个新的对象,实例对象的原型a.__proto__却仍然引用着原来它被创建时A的原型属性
var A = function(name) {
this.name = name;
}
var a = new A('alpha');
a.name; //'alpha'
A.prototype = {x:23};
a.x; //null

即如果在实例被创建之后,改变了函数的原型属性所指向的对象,也就是改变了创建实例时实例原型所指向的对象
但是这并不会影响已经创建的实例的原型。

一个默认的原型是什么样子的?
var A = function() {};
A.prototype.constructor == A; //true
var a = new A();
a.constructor == A; //true (a's constructor property inherited from it's prototype)

instanceof 原型,如果a的原型属于A的原型链,表达式a instanceof A

var A = function() {}

var a = new A();

a.__proto__ == A.prototype; //true - so instanceof A will return true

a instanceof A; //true;

//mess around with a's prototype

a.__proto__ = Function.prototype;

//a's prototype no longer in same prototype chain as A's prototype property

a instanceof A; //false

告诉我继承是怎样通过原型来工作的。什么是原型链?

因为每个对象和原型都有一个原型(注:原型也是一个对象),对象的原型指向对象的父,而父的原型又指向父的父,我们把这种通过原型层层连接起来的关系撑为原型链。这条链的末端一般总是默认的对象原型。

a.__proto__ = b;

 

 b.__proto__ = c;

 

 c.__proto__ = {}; //default
object

 

{}.__proto__.__proto__; //null

原型的继承机制是发生在内部且是隐式的.当想要获得一个对象a的属性foo的值,javascript会在原型链中查找foo的存在,如果找到则返回foo的值,否则undefined被返回。

赋值呢?原型的继承 is not a player 当属性值被设置成a.foo=’bar’是直接给a的属性foo设置了一个值bar。为了把一个属性添加到原型中,你需要直接指定该原型。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: