原型和原型链
原型和原型链
一、构造函数
构造函数模式的目的就是为了创建一个自定义类,并且创建这个类的实例。构造函数模式中拥有了类和实例的概念,并且实例和实例之间是相互独立的,即实例识别。
构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写。另外就是调用方式的不同,普通函数是直接调用,而构造函数需要使用new关键字来调用。
function Person(name, age, gender) { this.name = name this.age = age this.gender = gender this.sayName = function () { alert(this.name); } } var per = new Person("a", 12, "女"); function Dog(name, age, gender) { this.name = name this.age = age this.gender = gender } var dog = new Dog("小花", 4, "雌") console.log(per) console.log(dog);
二、原型链
1
.__proto__和constructor
每一个对象数据类型(普通的对象、实例、prototype…)也天生自带一个属性__proto__,属性值是当前实例所属类的原型(prototype)。原型对象中有一个属性constructor, 它指向函数对象。
function Person() {} var person = new Person() console.log(person.__proto__ === Person.prototype)//true console.log(Person.prototype.constructor===Person)//true
//顺便学习一个ES5的方法,可以获得对象的原型
console.log(Object.getPrototypeOf(person) === Person.prototype) // true
2.何为原型链
在JavaScript中万物都是对象,对象和对象之间也有关系,并不是孤立存在的。对象之间的继承关系,在JavaScript中是通过prototype对象指向父类对象,直到指向Object对象为止,这样就形成了一个原型指向的链条,专业术语称之为原型链。
举例说明:person → Person → Object ,普通人继承人类,人类继承对象类
当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。如果没有则去原型的原型中寻找,直到找到Object对象的原型,Object对象的原型没有原型,如果在Object原型中依然没有找到,则返回undefined。
我们可以使用对象的hasOwnProperty()来检查对象自身中是否含有该属性;使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true
function Person() {} Person.prototype.a = 123; Person.prototype.sayHello = function () { alert("hello"); }; var person = new Person() console.log(person.a)//123 console.log(person.hasOwnProperty('a'));//false console.log('a'in person)//true
person实例中没有a这个属性,从 person 对象中找不到 a 属性就会从 person 的原型也就是 person.proto ,也就是 Person.prototype中查找,很幸运地得到a的值为123。那假如 person.__proto__中也没有该属性,又该如何查找?
当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层Object为止。Object是JS中所有对象数据类型的基类(最顶层的类)在Object.prototype上没有__proto__这个属性。
console.log(Object.prototype.proto === null) // true
三、所有对象的__proto__
都指向其构造器的prototype
- 下面再看看所有这些构造器的实例对象的
__proto__
指向谁?
-
先看看JavaScript引擎内置构造器
var obj = {name: 'jack'} var arr = [1,2,3] var reg = /hello/g var date = new Date var err = new Error('exception') console.log(obj.__proto__ === Object.prototype) // true console.log(arr.__proto__ === Array.prototype) // true console.log(reg.__proto__ === RegExp.prototype) // true console.log(date.__proto__ === Date.prototype) // true console.log(err.__proto__ === Error.prototype) // true
-
再看看自定义的构造器,这里定义了一个Person
js function Person(name) { this.name = name } var p = new Person('jack') console.log(p.__proto__ === Person.prototype) // true
-
每个对象都有一个constructor属性,可以获取它的构造器,因此以下打印结果也是恒等的
console.log(p.__proto__ === Person.prototype) // true console.log(p.__proto__ === p.constructor.prototype) // true
-
上面的Person没有给其原型添加属性或方法,这里给其原型添加一个
getName
方法function Person(name) { this.name = name; } // 修改原型 Person.prototype.getName = function () {} var p = new Person("jack") console.log(p.__proto__ === Person.prototype) // true console.log(p.__proto__ === p.constructor.prototype) // true
可以看到
p.__proto__
与Person.prototype,p.constructor.prototype都是恒等的,即都指向同一个对象。 -
如果换一种方式设置原型,结果就有些不同了
function Person (name) { this.name = name } // 重写原型 Person.prototype = { getName : function () {} } var p = new Person("jack") console.log(p.__proto__ === Person.prototype) // true console.log(p.__proto__ === p.constructor.prototype) // false
理解 : 这里手动修改了Person.prototype
的指向, 并且指向了一个对象字面量 - 原来的
Person.prototype
是由new
关键字创建的 (Object.create(Person.prototype)
)Person.prototype
里面的constructor
属性指向的是 Person - 但是自从改了他的指向之后.他的新指向指的是一个对象字面量, 而这个对象字面量是通过new Object()创建的,所以新指向的空间的constructor的值是Object,而不是Person;
-
上面代码中用到的
__proto__
目前在IE6/7/8/9
中都不支持.IE9
中可以使用Object.getPrototypeOf(ES5)
获取对象的内部原型.
- js原型与原型链
- JavaScript中的原型与原型链
- js 原型和原型链
- JavaScript深入之从原型到原型链
- 深入理解JavaScript系列(5):强大的原型和原型链
- JS原型和原型链
- 原型和原型链
- JavaScript中的原型和原型链
- 前端基础之原型和原型链
- 彻底了解原型与原型链
- 谈谈我对js原型与原型链的一知半解
- JavaScript原型与原型链
- 深入理解JavaScript系列(5):强大的原型和原型链
- js中的原型以及原型链
- JS原型及原型链
- 深入理解JavaScript系列(5):强大的原型和原型链
- JavaScript原型和原型链
- 原型链的结构(深入理解原型继承)和举例
- 对象,原型,原型链
- 原型 和 原型链