一些应该掌握的知识点
当任意一个普通函数用于创建一类对象时,这个函数就被称作构造函数
构造函数原理案例分析(注:此案例及分析引用自简书 Yi罐可乐:最详尽的 JS 原型与原型链终极详解,没有「可能是」。(一)
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function() { alert(this.name) }
}
var person1 = new Person('Zaxlct', 28, 'Software Engineer');
var person2 = new Person('Mick', 23, 'Doctor');
上面的例子中person1和person2都是Person的实例。这两个实例都有一个constructor(构造函数)属性,该属性(是一个指针)指向Person。即:
console.log(person1.constructor == Person); //true
console.log(person2.constructor == Person); //true
我们要记住两个概念(构造函数,实例):person1和person2都是构造函数Person的实例。
一个公式:实例的构造函数属性(constructor)指向构造函数。
对象可通过属性consructor来查看自己的构造函数
原型(prototype)是函数对象(Function)的一个属性,它定义了构造函数制造出的对象的公共祖先,通过该构造函数产生的对象,可以继承该原型的属性和方法(注:需要注意的是,其实原型也是一种对象)。
原型原理案例分析
function carFactory (str) { //设定一个车工厂函数
this.name = 'BMW';
}
var oCar = new carFactory(); //用构造函数的方法早一辆车
console.log(oCar.color); //试图让控制台显示一个oCar所没有的color属性,最终控制台显示undefined
如上例所示,由于JavaScript是一种弱类型语言,当你在一个对象下直接使用一个没有定义的属性,该属性值会为undefined,用控制台查看该属性时不会报错。
function carFactory (str) {
this.name = 'BMW';
}
carFactory.prototype.color = 'black'; /* 用原型给车工厂函数的增加一个color属性,
并给该属性赋予字符串'black' */
carFactory.prototype.door = door || 4;/* 用原型给车工厂函数的增加一个door属性,
并给该属性赋予door,默认赋予数值4 */
var oCar1 = new carFactory(); //用构造函数的方法造车
var oCar2 = new carFactory();
console.log(oCar1.color); //控制台显示black
console.log(oCar2.door); //控制台显示4
如首例所示,当控制台需要查看oCar的color属性时发现oCar自己并没有这个属性,这时候oCar就回去找原型,即车工厂函数的对象,由于车工厂函数的对象并没有color属性,所以最终控制台显示undefined,但如果像上例所示,利用原型给车工厂函数创建一个color属性并给该属性赋予字符串’black’,最终控制台就显示black,door属性同理。
从以上案例可以看出,在原型上所添加的任何属性都是给祖先所添加的,当祖先的子类需要使用这些属性的时候,由于这些属性已经通过原型来添加到祖先上了,所以子类都可以使用这些属性。
- 不能通过修改后代修改祖先的内容
function sample () {
this.x = 1;
}
var sample1 = new sample();
var sample2 = new sample();
var sample3 = new sample();
sample1.x = 2; //修改sample1中x属性的值
delete sample2.x; //删除sample2中的x属性
console.log(sample1.x); //控制台显示2
console.log(sample2.x); //控制台显示undefined
console.log(sample3.x); //控制台显示1
- 可以利用原型的概念与特性来提取共有属性
shoeFactory.prototype.size = 40; //在创建鞋工厂函数前先用原型创建一些属性
shoeFactory.prototype.type = 'slipper';
function shoeFactory (color) { //创建一个鞋工厂函数
this.color = color || 'white'; //创建一个color属性,并给该属性赋予默认值'white'
}
shoe1 = new shoeFactory('red'); //用构造函数造鞋,设定新鞋的color属性的值为'red'
shoe2 = new shoeFactory('yellow'); //设定新鞋的color属性的值为'yellow'
shoe3 = new shoeFactory('blue'); //设定新鞋的color属性的值为'blue'
console.log(shoe1.color); //控制台显示red
console.log(shoe2.size); //控制台显示40
console.log(shoe3.type); //控制台显示slipper
注:通过以上方法,即提取共有特性的方法,可以缩短每次用构造函数创建新对象所调用函数的长度
__proto__
属性__proto__
属性是一种隐式属性,相对的prototype属性则是一种显示属性__proto__
属性并不标准,一般不提倡使用- 一般的,
__proto__
属性一般指向创建这个对象的函数的显式原型,而Object.creat()
所创建的对象的__proto__
属性指向这个对象的函数的显示原型的隐式原型 - 对象可通过
__proto__
属性来查看自己的原型 - 构造函数中的this =
{ __proto__: 函数名.protoype}
- 关于prototype、
__proto__
和 constructor三者关系分析的一篇博客参考:博客园 小火柴的蓝色理想:一张图理解prototype、proto和constructor的三角关系
关于
__proto__
属性一般情况下的指向的案例
function Sample () {
this.o = 0;
}
Sample.prototype.x = 1;
var oSample = new Sample();
oSample.__proto__.x = 2;
console.log(oSample.x); //控制台显示2,说明使用__proto__属性也可修改自己的相关属性
- 对象实例的原型未必非得是自己构造函数的原型(趣解:真正意义上的认贼作父)
function Adidas () {}; //设定一个阿迪达斯函数
Adidas.prototype.name = 'adidas'; //给自己的品牌命名为adidas
function Nike () {}; //设定一个耐克函数
Nike.prototype.name = 'nike'; //给自己的品牌命名为nike
var oNike = new Nike(); //用构造函数造鞋
console.log(oNike.name); //控制台显示nike
oNike.__proto__ = Adidas.prototype; /* 把阿迪达斯的显示原型
赋予nike的隐式原型 */
console.log(oNike.name); //因为原本耐克对象实例oNike的原型
已经被替换为阿迪达斯,所以控制台
显示adidas */
原型链
- 因为每个对象和原型都有原型,对象的原型指向原型对象,而父的原型又指向父的父,这种原型的层层连接就是原型链
- 原型链与原型终端 注:原型终端即原型链中的最顶层,一般是object()
- 原型链的形成是真正是靠
__proto__
属性而非prototype属性 一个原型链案例
Grand.prototype.lastname = 'Lin'; function Grand () {}; var grand = new Grand(); Father.prototype = grand; function Father () { // this.fortune = {money: 100}; this.fortune = 100; }; var father = new Father(); Son.prototype = father; function Son () {} var son = new Son(); // son.fortune.money += 100; son.fortune += 100; /* 对象son下的属性自加100,由于对象son下 并没有fortune属性,于是它会沿着原型链 网上找,最终它在祖先father对象中找到了 fortune属性,于是对象son继承了这个属性, 并执行了`son.fortune += 100;`这个语句 console.log(son.fortune); //控制台显示200 console.log(father.fortune); /* 由于对象son继承的属性改变并不 影响父类father对象的属性值,控 制台显示100 */ console,log(son); //控制台显示对象son
下图为上例代码末行
console.log(son);
的执行结果,从中可见原型链的端倪
关于原型与原型链的一篇博客参考:简书 Yi罐可乐:最详尽的 JS 原型与原型链终极详解,没有「可能是」
(注,该博文分为三节,本链接为第一节,其他两节在博文内容首行有链接)
一些练习
- 练习一
Person.prototype.name = 'sunny';
function Person () {}
Person.prototype.name = 'cheery';
var oPerson = new Person();
console.log(oPerson.name); //控制台显示cherry
- 练习二
Person.prototype.name = 'sunny';
function Person () {}
Person.prototype = {
name:'cherry'
};
var oPerson = new Person();
console.log(oPerson.name); //控制台显示cherry