本文是学习慕课网上课程前端跳槽面试必备技巧的学习笔记,便于之后复习。
从以下几个方面介绍原型链:
- 创建对象有几种方法
- 原型、构造函数、实例、原型链
- instanceof的原理
- new运算符
1.创建对象的方法
相应代码:
<script type="text/javascript">
// 方式一:字面量
var o1 = {name: 'o1'};
var o2 = new Object({name:'o2'});
// 方式二:通过构造函数
var M = function(name){this.name = name;};
var o3 = new M('o3');
// 方式三:Object.create
var p = {name: 'o4'};
var o4 = Object.create(p);
console.log(o1);
console.log(o2);
console.log(o3);
console.log(o4);
</script>
运行效果如下图:
每种创建方式,创建出来的对象也不相同。
2.原型、构造函数、实例、原型链
什么是原型对象?实例?构造函数?
先看一段代码:
var M = function (name) { this.name = name; }
var o3 = new M('o3')
任何通过new操作后的函数就是构造函数,构造函数也是函数。
实例就是对象,在本例中o3就是实例,M是构造函数。
实例通过new一个构造函数生成。
实例的__protpo__指向的是原型对象。
实例的构造函数的prototype也是指向原型对象。
原型对象的constructor(构造器)指向的是构造函数。//原型对象用构造器区分被那个构造函数所引用
什么是原型链呢?
简单理解就是原型组成的链,实例对象的__proto__是它的原型,而原型也是一个对象,也有__proto__属性,原型的__proto__又是原型的原型,就这样可以一直通过__proto__向上找,这就是原型链,当向上找找到Object的原型的时候,这条原型链就算到头了。
原型对象和实例之间有什么作用呢?
通过构造函数创建出很多属性和方法,实例可以共用这些属性和方法,当多个实例共用方法时,给每个实例都去添加方法并不是一个明智的选择。这时就该用上原型了。在实例的原型对象上添加一个方法,这个原型对象的所有实例便都有了这个方法。
代码举例:
<script type="text/javascript">
// 方式二:通过构造函数
var M = function(name){this.name = name;};
var o3 = new M('o3');
var o5 = new M('o5 ');
// 在M.prototype 即原型对象 上增加一个方法say o3和o5均可以访问到
M.prototype.say = function () {
console.log('say');
}
o3.say();
o5.say();
// 在o3.__proto__ 即原型对象 上增加一个方法say1 o3和o5均可以访问到
o3.__proto__.say1 = function () {
console.log('say1');
}
o3.say1();
o5.say1();
</script>
按照JS引擎的分析方式,在访问一个实例的属性的时候,现在实例本身中找,如果没找到就去它的原型中找,还没找到就再往上找,直到找到。这就是原型链。
补充:
1).只有函数有prototype,对象是没有prototype的。
2).对象有__proto__,但是函数也是有__proto__的,因为函数也是对象。函数的__proto__指向的是Function.prototype。也就是说普通函数是Function这个构造函数的一个实例。
3.instanceof的原理?
instanceof是判断实例对象的__proto__和生成该实例的构造函数的prototype是不是引用同一个地址。
仍是上一段代码
注意:实例的instanceof在比较的时候,与原型链上向上找到的构造函数相比都是true。
那怎么判断实例是由哪个构造函数生成的呢?
这时候就要用到constructor(构造器)了。实例的原型的构造器指向构造函数 obj.__proto__.constructor
4.new运算符工作原理?
new运算符的工作原理:
- 一个新对象被创建,新对象继承自foo.prototype // foo为构造函数
- 构造函数被执行。执行时,相应的传参会被传入,同时上下文(this)会被指定为这个新实例。在foo不传参的情况下 new foo等同于new foo()
- 如果构造函数返回了一个‘对象’,这个对象会取代整个new出来的结果
- 如果构造函数没有返回对象,那么new出来的结果为步骤1创建的对象
用代码模拟new运算符:
<script type="text/javascript">
// 模拟new 运算符
var new1 = function (func) {
// 1 创建空对象 空对象关联构造函数的原型对象
var o = Object.create(func.prototype);
// 2 执行构造函数 改变this的指向
var k = func.call(o);
// 3 判断构造函数的运行结果是否为对象类型
if(typeof k === 'object'){
// 是 返回k
return k
}else{
// 不是 返回构造函数的执行结果
return o
}
}
var M = function(name){this.name = name;};
var o3 = new M('o3');
var o5 = new M('o5 ');
</script>
通过上图我们可以看出,手动编写的new1和new运算符是一样的。