javascript

js原型、原型链

2017-12-13  本文已影响24人  YINdevelop

1.函数也是对象

有时我们会好奇为什么能给一个函数添加属性,函数难道不应该就是一个执行过程的作用域吗?

var fn=function(){
    this.name='手机'
}
fn.age=11
console.log(fn.age)  //11

console.log(fn)
// function(){
//  this.name='手机'
// }

其实,在JS里,函数就是一个对象,对象里面保存键值对。本例中fn为属性,属性值是函数的字符串,当调用函数执行时,js会将该字符串解析成可执行语句。

2.构造函数和普通函数的区别

function fn(){
    this.name='手机'
    console.log(this)
}
fn()

//此时这里面的this表示window

var m=new fn()

//此时this表示m对象。

3.函数和原型的关系

function Fn(name){
    this.name=name
    this.kind='电子产品'
}
var m1=new Fn('手机');
var m2=new Fn('电脑');

结果会生成两个m1,m2对象。

m1={
    name:'手机',
    kind:'电子产品'
}
m2={
    name:'电脑',
    kind:'电子产品'
}
//每一个实例对象,都有自己的属性和方法的副本。修改任何一个都不会影响另一个。
这不仅无法做到数据共享,也是极大的资源浪费。

function Fn(name){
    this.name=name
}
Fn.prototype = { kind : '电子产品' };
var m1=new Fn('手机');
var m2=new Fn('电脑');

结果会生成两个m1,m2对象。

m1={
    name:'手机',
    kind:'电子产品'
}
m2={
    name:'电脑',
    kind:'电子产品'
}
//kind属性放在prototype对象里,是两个实例对象共享的。只要修改了prototype对象,
就会同时影响到两个实例对象。

了解了原型和constructor指针后,我们通过一个例子以及一张图来进一步了解这两者的关系。

function Person(){
    this.age=10
}

Person.prototype.name = 'Nicholas';
Person.prototype.age = 24;
Person.prototype.sayAge = function () {
    alert(this.age);
};
1.png

4.原型和实例的关系

function Person(){
    this.age=10
}

Person.prototype.name = 'Nicholas';
Person.prototype.age = 24;
Person.prototype.sayAge = function () {
    alert(this.age);
};

//实例
var person1 = new Person('Lee');
var person2 = new Person('Lucy');

我们新建了两个实例person1和person2,这些实例的内部都会包含一个指向其构造函数的原型对象的指针(内部属性),不是对外访问的API,这个指针叫[[Prototype]],在ES5的标准上没有规定访问这个属性,所以在浏览器上是不能console出这个属性的,会报错。但是大部分浏览器通过proto的属性来访问它,成为了实际的通用属性,于是在ES6的附录里写进了该属性。

prototype和proto都叫原型,如果非要区分,prototype我们称为显示原型,proto我们称为隐式原型。

22.png
person1.__proto__ == Person.prototype // true

4.原型链

function Person(){
    this.age=10
}

Person.prototype.name = 'Nicholas';
Person.prototype.age = 24;
Person.prototype.sayAge = function () {
    alert(this.age);
};

//实例
var person1 = new Person();

前面的demo中我们举了一个类似这样的例子。
我们尝试输出实例的属性。

person1.age; // 10
person1.name; // Nicholas
person1.toString; // function toString() { [native code] }
person1

我们输出一下person1试试

im.jpg

从上例中,我们可以看出,Person有自己的原型,即Person.prototype,同样Person.prototype也有自己的原型,即Person.prototype.proto属性,我们输出下试试。

console.log(Person.prototype)
console.log(Person.prototype.__proto__)
console.log(Person.prototype.__proto__.__proto__)

结果如下。

img1.jpg

我们来总结下原型链。

当我们访问实例对象的一个属性时候,如果不存在,就去实例的原型对象里面找,这个原型对象也有自己的原型对象。就这样一直找就构成了我们常说的线性原型链。

上面代码的age来自于自身属性,name来自于原型属性,toString( )方法来自于Person原型对象的原型Object的原型。当我们访问一个实例属性的时候,如果没有找到,我们就会继续搜索实例的原型,如果还没有找到,就递归搜索原型链直到原型链末端。

我们来验证下

Person.prototype.__proto__ == Object.prototype // true

继续深入验证

Person.__proto__ == Function.prototype // true
Function.prototype.__proto__ == Object.prototype // true

我们会发现Person是Function对象的实例,Function是Object对象的实例,Person原型是Object对象的实例。

最后,奉上一张原型大图

33.jpg

最后的最后,说了这么多,实际怎么用?大家下去可以看一下,js面向对象的三大特性:
封装、继承、多态

原型链主要用于继承特性。
参考来源https://segmentfault.com/a/1190000011389965

上一篇 下一篇

猜你喜欢

热点阅读