导航
导航
文章目录
  1. 对象
  2. 函数和对象的关系
  3. prototype原型
  4. instanceof
  5. 原型链

JavaScript原型学习笔记

要想理解JavaScript中的原型,首先要从对象说起。

对象

JavaScript中一切都是对象,相信这句话大家都不陌生,但是其实在JavaScript中这句话并不准确。JavaScript中的typeof函数可以返回变量的类型。总共有两大类型,值类型(包括undefined、number、string、boolean)和引用类型(包括function、object)。只有引用类型的变量才能算是对象。函数、数组、对象、null、new Number(10)都是对象,他们都是引用类型。

JavaScript中对象是若干属性的集合,只有属性没有方法,方法也是一种属性。下面是一个常见的对象的例子:

1
2
3
4
5
6
7
8
9
10
var obj = {
a: 10,
b: {
name: 'hieeyh',
year: 1993
}
c: function(x) {
console.log(this.a + x);
}
}

属性a是一个数值,属性b是一个对象,属性c是一个函数。

函数和对象的关系

上面讲到函数也是一种对象,但是函数和对象之间的关系比较复杂,因为对象不仅可以通过上述方式创建还可以通过函数创建,如下:

1
2
3
4
5
6
function Person() {
this.name = 'hieeyh';
this.year = 1993;
this.sex = '女';
}
var person = new Person();

其实对象那一节所讲的对象创建方式的本质是:

1
2
3
4
5
6
7
8
//var obj = { a: 10, b: 20};
//var arr = [5, 6];
var obj = new Object();
obj.a = 10;
obj.b = 20;
var arr = new Array();
arr[0] = 5;
arr[1] = 6;

其中Object和Array都是函数。

到此我们得到一个很玄乎的概念,对象是由函数创建的,而函数却又是一种对象。想要搞清他们之间的关系就要进入我们的正题原型的概念了。

prototype原型

函数是一种对象,对象是若干属性的集合,所以函数一定有属性。每一个函数都有一个prototype属性,该属性的值又是一个对象。默认的只有一个叫做constructor的属性,指向这个函数本身。

prototype

如上图,myFunction是一个函数,右侧方框是它的原型。原型可以自定义增多很多属性,例如Object函数的原型中有hasOwnProperty、isPrototypeOf等属性。所以,我们可以在自定义的函数的prototype中增加自己的属性。如下:

1
2
3
4
5
6
function() Person() {}
Person.prototype.name = 'hieeyh';
Person.prototype.getYear = function() {
return 1993;
};
var person = new Person();

上述代码中,Person是一个函数,person是从Person函数中new出来的,person对象就可以调用Person.prototype中的属性。因为每个对象都有一个隐藏的属性“__proto__”,这个属性引用了创建这个对象的函数的prototype。即:person.__proto__ === Person.prototype。这里的”__proto__“称为隐式原型。

__proto__是一个隐藏的属性,开发者用不到这个属性值。既然每个对象都有一个__proto__属性,那么Object.prototype也是一个对象,它的prototype是什么呢?Object.prototype是一个特例,它的__proto__指向null。

函数也是一种对象,函数当然也有__proto__。函数的__proto__指向Function.prototype,因为函数对象都是由Function创建的。

当然Function也是一个函数,函数是一种对象,也有__proto__属性。既然是函数,那么它一定是被Function创建。所以Function是被自身创建的。所以它的__proto__指向了自身的Prototype。(有点晕,哈哈)

最后,Function.prototype指向对象,它的__proto__也指向Object.prototype。因为Function.prototype指向的对象也是一个普通的被Object创建的对象,所以也遵循基本的规则。

instanceof

typeof函数可以很清楚判断一个值类型变量的类型,但是对于引用类型的变量它只能判断出是object还是function,不能判断他具体是通过那个函数new出来的。这只就需要用到instanceof了。

instanceof运算符的第一个变量是一个对象,称为VAR;第二个变量一般是一个函数,称为FN。
instanceof的判断队则是:沿着VAR的__proto__这条线来找,同时沿着FN的prototype这条线来找,如果两条线能找到同一个引用,即同一个对象,那么就返回true。如果找到终点还未重合,则返回false。通过下面的例子进行进一步理解。

1
2
3
4
5
6
7
function Person() {}
var person = new Person();
console.log(person instanceof Person); //true
console.log(person instanceof Object); //true
console.log(Object instanceof Function); //true
console.log(Function instanceof Object); //true
console.log(Function instanceof Function); //true

有过面向对象变成经验的同学应该可以想到,instanceof表示的就是一种继承关系,在js中也可以说是一种原型链的结构。

原型链

1
2
3
4
5
6
7
function Foo() {}
var foo = new Foo();
foo.a = 10;
Foo.prototype.a = 100;
Foo.prototype.b = 200;
console.log(foo.a); //10
console.log(foo.b); //200

以上代码中,foo是Foo函数new出来的对象,foo.a是foo对象的基本属性,foo.b是从Foo.prototype得来,因为foo.__proto__指向的是Foo.prototype。

访问一个对象的属性时,先在其基本属性中查找,如果没有,再沿着__proto__这条链向上找,这就是原型链。访问foo.b时,f1的基本属性中没有b,于是沿着__proto__找到了Foo.prototype.b。

另外,所有的对象的原型链都会找到Object.prototype,因此所有的对象都会有Object.prototype的方法。这就是所谓的继承