在 Javascript 中原型和原型链是比较神奇也是比较让人难以理解的知识点,但是一旦弄懂了这部分的知识,也就正式迈入了进阶的门槛了。现在我就大概的把我所理解的写一下。等以后理解深入了,再修稿。
一、对象
在 Javascript 中,万物皆可对象,但是不同的对象也是分三六九等的。首先,Object 是顶级公民,从名字上看也懂,这是JS的’亲儿子’,一等公民就是 Function。其他剩下的 String,Array,Date,Number,Boolean,Math等内建对象都是’臭鱼烂虾’。但是大概的分类就是普通对象和函数对象
var o1 = {};
var o2 =new Object();
var o3 = new f1();
function f1(){};
var f2 = function(){};
var f3 = new Function('str','console.log(str)');
console.log(typeof Object); //function
console.log(typeof Function); //function
console.log(typeof f1); //function
console.log(typeof f2); //function
console.log(typeof f3); //function
console.log(typeof o1); //object
console.log(typeof o2); //object
console.log(typeof o3); //object
其实俩个分类的区分还是很简单的,只要通过 New Function() 的方式创建的都是函数对象。
二、构造函数
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function() { alert(this.name) }
}
var p1 = new Person('Zaxlct', 28, 'Software Engineer');
var p2 = new Person('Mick', 23, 'Doctor');
这个例子里面,P1和P2都是 Person 的实例,这两个实例都有一个 constructor
(构造函数)属性,该属性(是一个指针)指向 Person。
console.log(p1.constructor == Person); //true
console.log(p2.constructor == Person); //true
在 Chrome 的命令行中就能打印中其他的关系(是有联系的),总结一句话就是:实例的构造函数属性(constructor)指向构造函数
三、原型
在 JavaScript 中,每当定义一个对象的时候,对象中都会包含一些预定义的属性。其中每个对象肯定都包含有一个 prototype 属性 ( prototype 本身也是对象 ),这个属性指向函数的原型对象。
function Person() {}
Person.prototype.name = 'nys';
Person.prototype.age = 28;
Person.prototype.job = 'Software Engineer';
Person.prototype.sayName = function() {
alert(this.name);
}
var person1 = new Person();
person1.sayName(); // 'nys'
var person2 = new Person();
person2.sayName(); // 'nys'
这个例子就是使用原型模式构建了原型对象,其实原型对象就是 Person.prototype
。当然,上面的构造方面也有简写模式:
Person.prototype={
name: 'nys',
age: 80,
job: 'Software Engineer',
sayName: function(){
alert(this.name)
}
在上面的例子中,定义了 name
, age
, job
, sayName()
四个属性,但是不能忽略他的默认属性:constructor
,但是它是干嘛的呢?大概就是这样:
Person.prototype.constructor == Person
记住一点:constructor
这个玩意儿是个指针,指向 Person
。
总结:
Person 构造函数
Person.prototype 原型对象
constructor 属性(原型对象和实例都会拥有),指向构造函数
p1 为什么有 constructor
属性?那是因为 p1 是 Person 的实例。
那 Person.prototype
为什么有 constructor
属性?同理, Person.prototype
(你把它想象成 A) 也是Person 的实例。
也就是在 Person 创建的时候,创建了一个它的实例对象并赋值给它的 prototype。
四、proto
JS 在创建对象的时候,都有一个叫做__proto__
的内置属性,用于指向创建它的构造函数的原型对象。对象 p1 有一个 __proto__
属性,创建它的构造函数是 Person
,构造函数的原型对象是 Person.prototype
,所以:
person1._proto_== Person.prototype
//大概就是这样的关系
Person.prototype.constructor == Person;
person1.__proto__ == Person.prototype;
person1.constructor == Person;
五、原型链
5.1 什么是原型链?
原型链的核心就是依赖对象的 _proto_
的指向,当自身不存在的属性时,就一层层的扒出创建对象的构造函数,直至到Object时,就没有 _proto_
指向了。
5.2 如何分析原型链?
因为 _proto_
实质找的是 prototype
,所以我们只要找这个链条上的构造函数的 prototype
。其中 Object.prototype
是没有 _proto_
属性的,它==null。
5.3 最简单的原型链分析
function Person(name){
this.name = name;
}
var p = new Person();
//p ---> Person.prototype --->Object.prototype---->null
5.4 属性搜索原则:
- 当访问一个对象的成员的时候,会现在自身找有没有,如果找到直接使用。
- 如果没有找到,则去原型链指向的对象的构造函数的prototype中找,找到直接使用,没找到就返回undifined或报错。
5.5 原型继承
//原型继承的基本案例
function Person(name, age) {
this.name = name;
this.age = age;
}
//1.直接替换原型对象
var parent = {
sayHello : function() {
console.log("方式1:替换原型对象");
}
}
Person.prototype = parent;
var p = new Person("张三", 50);
p.sayHello();
//2.混入式原型继承
console.log(".............混入式原型继承..............");
function Student(name, age) {
this.name = name;
this.age = age;
}
var parent2 = {
sayHello : function() {
console.log("方式2:原型继承之混入式加载成员");
}
}
for ( var k in parent2) {
Student.prototype[k] = parent2[k];
}
var p = new Student("张三", 50);
p.sayHello();