原型链
创建对象的几种方式
- 对象字面量
var obj1 = {
name:'李思安',
age:20,
sayHi:function(){
alert('hi')
}
}
- 通过new操作符+Object类
var obj2 = new Object();
obj2.name = '王小二',
obj2.age = 25,
obj2.sayName = function(){
alert(this.name)
}
以上两种方式在使用同一接口创建多个对象时,会产生大量重复代码,为了解决此问题,工厂模式被开发
- 工厂模式
function createPerson(name,age) {
var o = new Object();
o.name = name;
o.age = age;
o.sayName = function(){
alert(this.name)
}
return o
}
var obj3 = createPerson('夏雨',30);
// instanceof无法判断它是谁的实例,只能判断他是对象,构造函数都可以判断出
console.log(obj3 instanceof Object) // true
工厂模式解决了重复实例化多个对象的问题,但是没有解决对象识别的问题(工厂模式无法识别对象的类型,因为都是Object,不像Date,Array等,因此出现了
构造函数模式
)
- 构造函数
// 构造函数模式
function Person(name,age) {
this.name = name;
this.age = age;
this.sayName = function(){
alert(this.name);
}
}
var obj4 = new Person('夏雪',28);
console.log(obj4 instanceof Object) // true
console.log(obj4 instanceof Person) // true
对比工厂模式有一下不同之处
- 没有显示的创建对象
- 直接将属性和方法赋值给了this 对象
- 没有return语句
以此方法调用构造函数的步骤:
- 创建一个新对象
- 将构造函数的作用域赋给新对象(将this指向这个新对象)
- 执行构造函数代码(为这个新对象添加属性)
- 返回新对象
可以看出,构造函数知道自己从哪里来(通过 instanceof 可以看出其既是Object的实例,又是Person的实例)
构造函数也有其缺陷,每个实例都包含不同的Function实例(函数也是对象),这些函数实例做的都是同样的事情,但是却都是单独的一个函数,因此产生了原型模式
- 原型模式
// 原型模式
function Person(){}
Person.prototype.name = '夏天';
Person.prototype.age = 35;
Person.prototype.sayName = function(){
alert(this.name)
}
var obj5 = new Person()
console.log(obj5.name,obj5.age) // 夏天 35
var obj6 = new Person()
obj6.name = '夏冰雹';
obj6.sayName = function(){
alert('hi' + this.name)
}
console.log(obj6.name); // "夏冰雹"
console.log(obj6.age); // 35
console.log(obj6.sayName()); // hi 夏冰雹
原型模式的好处是所有对象实例共享它的属性和方法(即所谓的
共有属性
),此外还可以像obj6那样设置自己的属性和方法,即私有属性
,从而覆盖原型对象上的同名属性或方法
- 混合模式(构造函数模式+原型模式)
构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性
function Person(name,age) {
this.name = name;
this.age = age;
}
Person.prototype = {
constructor: Person,
sayName:function(){
alert(this.name)
}
}
var obj7 = new Person('逍遥',18);
console.log(obj7.sayName()) // 逍遥
var obj8 = new Person('灵儿', 18);
console.log(obj8.sayName()) //灵儿
可以看出,混合模式共享着对相同方法的引用,又保证了每个实例有自己的私有属性,最大限度的节省了内容
需要注意的一些概念
- 任何函数都可以用来当构造函数(通过new运算符),不用new就是一个普通函数,总之构造函数也是函数
- 每个函数都有一个 prototype属性,这个是在声明一个函数的时候,JS自动给它加上的属性,指向该函数的原型对象,而原型对象都有constructor属性(构造器),这是一个指向prototype属性所在函数的指针
借用网上的一张图来表示他们之间的关系
image.png- 原型的概念:每一个javascript对象(除null外)创建的时候,就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型中“继承”属性。
- 这是每个对象(除null外)都会有的属性,叫做
__proto__
,这个属性会指向该对象的原型。 - 每个原型都有一个constructor属性,指向关联的构造函数。
原型链
从一个实例对象往上找构造这个实例的相关联的对象,然后这个关联对象再往上找,它又有创造它的上一级的原型对象,以此类型,一直到Object.prototype原型对象终止,这个链条就达到了顶端
那么原型链是通过什么来实现这个向上找的过程呢? 就是通过prototype原型
和__proto__属性
来完成原型链的查找
instanceof 就是判断一个实例是否属于某种类型
判断的原理:就是通过判断实例下的__proto__属性
和它的构造函数下的的prototype属性
是不是引用的同一个地址,如果是就返回true,不是就返回false
为什么会有instanceof
在 JavaScript 中,判断一个变量的类型尝尝会用 typeof 运算符,在使用 typeof 运算符时采用引用类型存储值会出现一个问题,无论引用的是什么类型的对象,它都返回 "object"。所以,ECMAScript 引入了另一个 Java 运算符 instanceof 来解决这个问题。