ES6 函数和对象(二)

JS函数复习

首先我们阐述几个事实

  • 函数即是对象,特殊的对象
  • 函数中的this根据调用的上下文环境决定
  • 只有在使用new运算符时,函数才是构造函数

1. 函数调用方式

  • 函数调用
    这个调用的上下文(this)为全局对象,ES5严格模式下,则是undefined
var isStrict = (function(){return !this;}()); 
  • 方法调用
    保存在对象中的函数即可以看做方法。
var o = {
 name: "jeff",
 hello: function(){
     console.log("hello!"+ "my name is " + this.name);
 }  
};

o.hello(); 

注意:嵌套函数的上下文并不是外层函数的上下文

  • 构造函数

通过new来调用函数,该函数就变成了构造函数。

new会执行以下操作

  1. 创建一个空对象{}
  2. 将该对象的委托给构造函数的prototype
  3. 将该对象作为call调用的参数,执行构造函数
function Person(){
   this.name = "jeff";
}

var p = new Person();
p.name
// "jeff" 

new操作类似于以下操作

function newit(f){
   var t = {};
   t.__proto__ = f.prototype;
   f.call(t);
   return t;
}

var x = newit(Person)
x.name
// "jeff" 
  • 间接调用
    我们可以用callapply对方法进行间接调用,call能接受好多个参数,第一个参数是函数执行的上下文,另外的参数都是函数执行的参数,而apply接受两个参数,一个参数是函数执行的上下文,一个参数是函数所需的参数组成的数组
Math.max.call(Math, 1, 2, 3, 4);
// 4
Math.max.apply(Math, [1,2,3,4,5]);
// 5 

2. 函数的实参和形参

  • 在调用函数时传入的实参比函数声明的形参少的话,剩下的形参会被设置为undefined
  • 在函数中存在一个标识符arguments(在严格模式下是保留字,无法修改),它指向实参的对象,是一个类数组对象,可以通过数组方法借用处理它。它可以有效解决实参大于形参时出现的问题。

3. callee和caller属性

实参对象还定义了calleecaller属性,callee指代当前正在执行的函数,caller则指代调用该函数的函数

function a(f){
   if(f) 
       arguments.callee(!f);
   else
       console.log("callee");
}
a(true);
// callee 

caller是规范外的参数,arguments.caller已经无法使用了,不过可以使用Function.caller这个属性如下:

function b(){ 
   console.log(b.caller); 
}
function c(){ 
   b(); 
}
c();
// function c(){ b(); } 

4. 函数闭包

将变量隐藏在函数作用域中,我们称为闭包

在实现闭包之前,你得知道词法作用域的一个特点:Javascript函数的执行用到了作用域链,这个作用域链是函数定义的时候创建的。所以如果函数定义在函数作用域中,其引用的变量一定是函数作用域中的变量。

不同于C语言这种底层语言,作用域链就像一个巨大的对象列表,每次调用Javascript函数时,都会创建一个新的对象来保存局部变量,然后把对象添加到作用域链中。当函数返回时,就会从作用域中把对象移除。如果不存在嵌套函数,也没有其他引用指向这个绑定对象,它会被垃圾回收;如果存在嵌套函数,并且这个嵌套函数被返回,或者存储在某个变量中时,这时这个对象不会被销毁。所以我们在外部执行嵌套函数时,仍然可以访问其所在词法作用域

5. bind()方法

ES5新增的方法,但是可以在ES3中实现。bind()方法就是为函数绑定一个对象作为上下文,而且它还有函数柯里化(curring)的功能 ,即函数预先绑定参数

var sum  = function(x,y){
   return x+y;
}
var sum1 = sum.bind(null, 1);
sum1(2);
// 3 

6. 函数的prototype和对象的constructor

new操作时,返回的对象的原型属性实际上引用函数的prototype

function a(){}
var b = new a();
b.__proto__ === a.prototype
// true 

在函数声明时,.prototype.contructor会引用函数对象,这是默认值。而当我们new操作时创建对象时,实际上对象有个contructor属性,它实际上引用自.prototype.contructor

b.constructor === a.prototype.constructor
// true 

不过需要注意的是,这个contructor实际上一点都不靠谱,随时可能被破坏掉。

待续