构造函数大写字母开头,可以使用new操作符创建实例。
1 | function Foo(name, age){ |
var a = {}是var a = new Object()的语法糖
var a = []是var a = new Array()的语法糖
function Foo(){}是var Foo = new Function(..)
instanceof是用于判断引用类型属于哪个构造函数的方法。比如上述代码,f是Foo的一个实例,判断方法是f的_proto_属性,一层一层向上,能否对应到Foo的显式原型对象。f instanceof Object也是正确的,需要向上追溯两层。
所有引用类型,包括数组对象函数都可以自由扩展属性(除了null对象)
1
2
3
4var obj = {};obj.a = 100;
var arr = [];arr.a = 100;
function fn () {}
fn.a = 100;都有一个_proto_(隐式原型)属性,为一个普通对象
1
2
3console.log(obj._proto_);
console.log(arr._proto_);
console.log(fn._proto_);所有的函数都有一个prototype(显式原型)属性,是一个普通对象即原型对象;而原型对象有一个constructor属性,用来指定该对象的构造函数,比如M是个构造函数,那么M.prototype.constructor === M
1
console.log(fn.prototype)
并且所有对象的隐式原型指向构造函数的显式原型
1
obj._proto_ === Object.prototype
试图得到一个对象的属性时,如果这个对象没有这个属性,就到他的_proto_(构造函数的显式原型)中寻找;
for in循环可以遍历对象的所有的可枚举属性,包括自身的和从构造函数原型继承的属性,有时候只想得到对象自身的属性而不是继承的属性,需要写如下代码:
1
2
3
4
5var item;
for (item in f) {
if(f.hasOwnProperty(item)){
console.log(item);
}对于如下这段代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16//构造函数
function Foo(name, age){
this.name = name
};
Foo.prototype.alertName=function(){
alert(this.name)
}
//创建实例
var f=new Foo('Zhangsam');
f.printName=function(){
console.log(this.name)
}
//测试
f.printName();
f.alertName();
f.toString();在上面这三种情况下,究竟分别是怎样找到对应的属性呢,这里面涉及到JavaScript中的属性查找规则,正如我们第九条中所言,对于printName而言,首先在f自身中寻找printName属性,发现有这个属性,那么就停止查找了;
然后对于alertName,情况就有意思一点了,因为f自身是没有alertName这个属性的,那么此时就需要到f的隐式原型也就是_proto_对象中查找这个属性,也就是在构造函数Foo的显式原型中寻找这个属性,发现我们刚好在上面有所定义,那么就结束查找;
对于第三种情况来说,情况就更有意思的多,我们在构造函数的原型对象中也没有发现toString方法,就需要继续沿着原型链向上查找,此时就需要找到Foo的构造函数的显示原型对象,而对象的构造函数就是Object,所以我们就找到了Object的原型对象上定义的toString方法;
而JavaScript为了避免死循环,规定Object.prototype.__proto__为null,即Object.__proto__就是整个原型链的顶端。
并且Function.prototype.proto===Object.prototype,这也说明了为什么函数就是对象。
12. 构造函数的显式原型对象上的方法是被所有实例共有的
如何创建一个对象
对象字面量和对象构造函数
1
2var o1 = {name: 'o1'};
var o11 = new Object({name: 'o11'});使用显示的构造函数创建对象
1
2
3
4var M = function () {
this.name = 'o2';
}
var o2 = new M();使用Object的create方法
1
2var p = {name: 'p'};
var o3 = Object.create(p);创建的对象是用原型链连接的,o3.proto === p
instanceof的原理
instanceof的原理就是判断实例对象的隐式原型对象_proto_是不是和构造函数的prototype属性引用自同一个对象。只要是在原型链上面的构造函数对于instanceof方法来说都会返回true。
instanceof由于会追溯原型链上面的对象,所以不够严谨,我们还有一种方法是通过实例对象的_proto_对象的constructor属性的指向来判断他的构造函数,这个构造器属性就指向实例对象的构造函数。
1 | function M(){}; |
如何准确判断一个变量是数组类型
利用instanceof Array
1 | var arr=[]; |
而typeof无法判断段是否是数组
写一个原型链继承的例子
1 | function Animal(){ |
描述new一个对象的过程
- 创建一个空对象,继承了构造函数的prototype对象
- 构造函数被执行。执行的时候,相应的参数被传入,同时this指向这个新实例(new Foo等同于new Foo())。
- 如果构造函数返回了一个“对象”,那么这个对象会取代整个new出来的结果。如果构造函数没有返回对象。那么new出来的结果是步骤1创建的对象。
下面用代码演示new运算符背后的工作原理
1 | var new2 = function (func) { |
在学习这个知识的时候,发现了一个很有意思的事情就是我们在使用new操作符去实例化一个构造函数的时候,如果构造函数明确返回一个对象,那么new构造函数才会返回那个对象;否则任何情况下都会返回实例化后的对象,这也是理解new操作符原理的关键。
zepto源码中如何使用原型链
下面是一个封装DOM查询的例子:
1 | function Elem(id){ |
2017 年 12月 31日