闭包,原型,原型链,继承
对象——若干属性的集合
typeof输出的集中类型标识,
其中上面的四种(undefined, number, string, boolean)属于简单的值类型,不是对象。
剩下的几种情况——函数、数组、对象、null、new Number(10)都是对象。他们都是引用类型。
判断一个变量是不是对象非常简单。值类型的类型判断用typeof,引用类型的类型判断用instanceof。
一切(引用类型)都是对象,对象是属性的集合
函数和对象的关系
(鸡生蛋蛋生鸡)
对象都是通过函数创建的
console.log(typeof (Object)); // function
console.log(typeof (Array)); // function
prototype原型
javascript默认的给函数一个属性——prototype。每个函数都有一个属性叫做prototype。
这个prototype的属性值是一个对象(属性的集合,再次强调!),默认的只有一个叫做constructor的属性,指向这个函数本身。
每个对象都有一个隐藏的属性——“__proto__”,这个属性引用了创建这个对象的函数的prototype。
这里的"__proto__"成为“隐式原型”
隐式原型__proto__
每个函数function都有一个prototype,即原型。
每个对象都有一个__proto__,可成为隐式原型。
(这个__proto__是一个隐藏的属性,javascript不希望开发者用到这个属性值,有的低版本浏览器甚至不支持这个属性值。所以你在Visual Studio 2012这样很高级很智能的编辑器中,都不会有__proto__的智能提示,但是你不用管它,直接写出来就是了。)
每个对象都有一个__proto__属性,指向创建该对象的函数的prototype
Object.prototype确实一个特例——它的__proto__指向的是null
Object.__proto__ === Function.prototype
Function是被自身创建的。所以它的__proto__指向了自身的Prototype
Function.prototype指向的对象,它的__proto__也指向Object.prototype
instanceof
typeof在判断到引用类型的时候,返回值只有object/function,你不知道它到底是一个object对象,还是数组,还是new Number等等。
这个时候就需要用到instanceof。
Instanceof运算符的第一个变量是一个对象,暂时称为A;第二个变量一般是一个函数,暂时称为B。
Instanceof的判断队则是:沿着A的__proto__这条线来找,同时沿着B的prototype这条线来找,如果两条线能找到同一个引用,即同一个对象,那么就返回true。如果找到终点还未重合,则返回false。
notice
一条线一条线挨着分析
instanceof表示的就是一种继承关系,或者原型链的结构
继承
javascript中的继承是通过原型链来体现的
访问一个对象的属性时,先在基本属性中查找,如果没有,再沿着__proto__这条链向上找,这就是原型链。
在实际应用中如何区分一个属性到底是基本的还是从原型中找到的呢?hasOwnProperty,特别是在for…in…循环中
由于所有的对象的原型链都会找到Object.prototype,因此所有的对象都会有Object.prototype的方法。这就是所谓的“继承”。
每个函数都有call,apply方法,都有length,arguments,caller等属性。为什么每个函数都有?这肯定是“继承”的。函数由Function函数创建,因此继承的Function.prototype中的方法。
hasOwnProperty是Function.prototype继承自Object.prototype的方法。
原型的灵活性
在Java和C#中,你可以简单的理解class是一个模子,对象就是被这个模子压出来的一批一批月饼(中秋节刚过完)。压个啥样,就得是个啥样,不能随便动,动一动就坏了。
而在javascript中,就没有模子了,月饼被换成了面团,你可以捏成自己想要的样子。
对象或者函数,刚开始new出来之后,可能啥属性都没有。但是你可以这会儿加一个,过一会儿在加两个,非常灵活。
Object和Array的toString()方法不一样。肯定是Array.prototype.toString()方法做了修改
如果你要添加内置方法的原型属性,最好做一步判断,如果该属性不存在,则添加。如果本来就存在,就没必要再添加了。
执行上下文
在一段js代码拿过来真正一句一句运行之前,浏览器已经做了一些“准备工作”,其中就包括对变量的声明,而不是赋值。变量赋值是在赋值语句执行的时候进行的。
我们总结一下,在“准备工作”中完成了哪些工作:
变量、函数表达式——变量声明,默认赋值为undefined;
this——赋值;
函数声明——赋值;
这三种数据的准备情况我们称之为“执行上下文”或者“执行上下文环境”。
这个“代码段”其实分三种情况——全局代码,函数体,eval代码。
函数每被调用一次,都会产生一个新的执行上下文环境。
函数在定义的时候(不是调用的时候),就已经确定了函数体内部自由变量的作用域。
给执行上下文环境下一个通俗的定义——在执行代码之前,把将要用到的所有的变量都事先拿出来,有的直接赋值了,有的先用undefined占个空。
this
this的取值,分四种情况
在函数中this到底取何值,是在函数真正被调用执行的时候确定的,函数定义的时候确定不了。
因为this的取值是执行上下文环境的一部分,每次调用函数,都会产生一个新的执行上下文环境。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。