一切都是对象
数组是对象,函数是对象,对象还是对象。对象里面的一切都是属性,只有属性,没有方法。那么这样方法如何表示呢?——方法也是一种属性。因为它的属性表示为键值对的形式。 对象:
var obj = { a:10, b: function(){ console.log(this.a) }}复制代码
函数
var fn = function () { alert(100); }; fn.a = 10; fn.b = function () { alert(123); }; 复制代码
函数和对象的关系
函数是一种对象
对象都是通过函数创建的
prototype 原型
每个函数都有一个属性叫做prototype。
这个prototype的属性值是一个对象(属性的集合,再次强调!),默认的只有一个叫做constructor的属性,指向这个函数本身
隐式原型
每个对象都有一个__proto__,可成为隐式原型。
每个对象都有一个__proto__属性,指向创建该对象的函数的prototype。
Object.prototype确实一个特例——它的__proto__指向的是null,切记切记!
还有——函数也是一种对象,函数也有__proto__。
对象的__proto__指向的是创建它的函数的prototype,就会出现:Object.proto === Function.prototype。用一个图来表示。
上图中,很明显的标出了:自定义函数Foo.__proto__指向Function.prototype,Object.__proto__指向Function.prototype,唉,怎么还有一个……Function.__proto__指向Function.prototype?这不成了循环引用了?
对!是一个环形结构。
其实稍微想一下就明白了。Function也是一个函数,函数是一种对象,也有__proto__属性。既然是函数,那么它一定是被Function创建。所以——Function是被自身创建的。所以它的__proto__指向了自身的Prototype。
最后一个问题:Function.prototype指向的对象,它的__proto__是不是也指向Object.prototype?
答案是肯定的。因为Function.prototype指向的对象也是一个普通的被Object创建的对象,所以也遵循基本的规则。
对于这一小节的知识点,总结起来就是以下几点:
-
Object 是所有对象的爸爸,所有对象都可以通过 proto 找到它
-
Function 是所有函数的爸爸,所有函数都可以通过 proto 找到它
-
函数的 prototype 是一个对象
-
对象的 __proto__属性指向原型, __proto__将对象和原型连接起来组成了原型链
instanceof
对于值类型,你可以通过typeof判断,string/number/boolean都很清楚,但是typeof在判断到引用类型的时候,返回值只有object/function,你不知道它到底是一个object对象,还是数组,还是new Number等等。
这个时候就需要用到instanceof。
Instanceof运算符的第一个变量是一个对象,暂时称为A;第二个变量一般是一个函数,暂时称为B。
Instanceof的判断队则是:沿着A的__proto__这条线来找,同时沿着B的prototype这条线来找,如果两条线能找到同一个引用,即同一个对象,那么就返回true。如果找到终点还未重合,则返回false。
原型链
访问一个对象的属性时,先在基本属性中查找,如果没有,再沿着__proto__这条链向上找,这就是原型链。
如何区分一个属性到底是基本的还是从原型中找到的呢?大家可能都知道答案了——hasOwnProperty,特别是在for…in…循环中,一定要注意
执行上下文
在一段js代码拿过来真正一句一句运行之前,浏览器已经做了一些“准备工作”,其中就包括对变量的声明,而不是赋值。变量赋值是在赋值语句执行的时候进行的。
我们总结一下,在“准备工作”中完成了哪些工作:
变量、函数表达式——变量声明,默认赋值为undefined
this——赋值
函数声明——赋值 这三种数据的准备情况我们称之为“执行上下文”或者“执行上下文环境”。
其实,javascript在执行一个代码段之前,都会进行这些“准备工作”来生成执行上下文。这个“代码段”其实分三种情况——全局代码,函数体,eval代码。
以上代码展示了在函数体的语句执行之前,arguments变量和函数的参数都已经被赋值。从这里可以看出,函数每被调用一次,都会产生一个新的执行上下文环境。因为不同的调用可能就会有不同的参数。
另外一点不同在于,函数在定义的时候(不是调用的时候),就已经确定了函数体内部自由变量的作用域。
this
在函数中this到底取何值,是在函数真正被调用执行的时候确定的,函数定义的时候确定不了
情况1:构造函数
如果函数作为构造函数用,那么其中的this就代表它即将new出来的对象。
,在构造函数的prototype中,this代表着什么。
如上代码,在Fn.prototype.getName函数中,this指向的是f1对象。因此可以通过this.name获取f1.name的值。
其实,不仅仅是构造函数的prototype,即便是在整个原型链中,this代表的也都是当前对象的值。
情况2:函数作为对象的一个属性
如果函数作为对象的一个属性时,并且作为对象的一个属性被调用时,函数中的this指向该对象
情况3:函数用call或者apply调用
当一个函数被call和apply调用时,this的值就取传入的对象的值
情况4:全局 & 调用普通函数
在全局环境下,this永远是window。
不过下面的情况你需要注意一下:
情况5:箭头函数中
包裹箭头函数的第一个普通函数中的this
执行上下文栈
执行全局代码时,会产生一个执行上下文环境,每次调用函数都又会产生执行上下文环境。当函数调用完成时,这个上下文环境以及其中的数据都会被消除,再重新回到全局上下文环境。处于活动状态的执行上下文环境只有一个。
其实这是一个压栈出栈的过程——执行上下文栈
作用域
作用域在函数定义时就已经确定了。而不是在函数调用时确定。
作用域只是一个“地盘”,一个抽象的概念,其中没有变量。要通过作用域对应的执行上下文环境来获取变量的值。同一个作用域下,不同的调用会产生不同的执行上下文环境,继而产生不同的变量的值。所以,作用域中变量的值是在执行过程中产生的确定的,而作用域却是在函数创建时就确定了。
所以,如果要查找一个作用域下某个变量的值,就需要找到这个作用域对应的执行上下文环境,再在其中寻找变量的值。
自由变量
在A作用域中使用的变量x,却没有在A作用域中声明(即在其他作用域中声明的),对于A作用域来说,x就是一个自由变量
闭包
你只需要知道应用的两种情况即可——函数作为返回值,函数作为参数传递。
第一,函数作为返回值
第二,函数作为参数被传递
讲到自由变量跨作用域取值时,曾经强调过:要去创建这个函数的作用域取值,而不是“父作用域”
参考 :