参考文章:

  • https://www.cnblogs.com/wangyuanliang/p/4280354.html
  • https://www.cnblogs.com/wangyuanliang/p/4264505.html

    一、属性

    1.1 arguments属性

    获取当前正在执行的 Function 对象的参数(arguments对象)。arguments是一个包含所有实参类数组对象,即arguments对象中包含了形参中未定义的参数。 ```javascript function testArg(paramA,paramB) { console.log(“形参:”,paramA, paramB); let str = “arguments:”; for(var i = 0; i < arguments.length; i++) {
    1. str += (arguments[i] + " ");
    } console.log(str); } testArg(“a”, “b”, “c”); //形参: a b //arguments:a b c
<a name="sFWab"></a>
## 1.2 caller属性
获取调用当**前函数的函数**。 如果函数是从 JavaScript 程序的顶层调用的,则 caller 包含 null。
```javascript
function callerFun(){
    console.log(callerFun.caller);
} 

function fun() {
    callerFun();
}

fun();
callerFun();

//ƒ fun() {
//    callerFun();
//}
//null

1.3 length属性

获取函数的参数数目。创建函数的实例后,脚本引擎将该函数的 length 属性初始化为该函数定义中的参数数量。

function lengthFun(arg1,arg2,arg3,arg4){
2   console.log("lengthFun's length="+lengthFun.length);
3   console.log("arguments' length="+arguments.length);
4 }
5 
6 lengthFun(1,2,3);

//lengthFun's length=4
//arguments' length=3

1.4 name属性

name 属性返回一个函数声明的名称。

function doSomething() { }
doSomething.name;  // "doSomething"

二、方法(不包括继承自Object对象的方法)

2.1 apply() 和 call()方法

apply()和call()都是定义在Function对象原型上的方法(Function.prototype.apply = function(){}&Function.prototype.call = function(){}),故可以直接使用。
注:call()方法的作用和 apply() 方法类似,区别就是**call()方法接受的是参数列表,而apply()方法接受的是一个参数数组**。

2.1.1 apply()

apply() 方法调用一个具有给定this值的函数,以及以一个数组(或类数组对象)的形式提供的参数。

语法 apply([thisObj[,argArray]])
参数
- thisObj 必选的。在 func 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 nullundefined 时会自动替换为指向全局对象,原始值会被包装。。
- argArray可选。 一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为 nullundefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象
备注 如果 argArray 不是有效对象,则会出现“应为对象”错误。如果既未提供 argArray 也未提供 thisObj,则原始 this 对象将被用作 thisObj 且不会传递任何参数
返回值 调用有指定**this**值和参数的函数的结果。

实例1:用 apply 将数组各项添加到另一个数组

我们可以使用push将元素追加到数组中。由于push接受可变数量的参数,所以也可以一次追加多个元素。
但是,如果push的参数是数组,它会将该数组作为单个元素添加,而不是将这个数组内的每个元素添加进去,因此我们最终会得到一个数组内的数组。如果不想这样呢?concat符合我们的需求,但它并不是将元素添加到现有数组,而是创建并返回一个新数组。 然而我们需要将元素追加到现有数组……那么怎么做好?难道要写一个循环吗?别当然不是!
apply正派上用场!

var array = ['a', 'b'];
var elements = [0, 1, 2];
array.push.apply(array, elements);
console.info(array); // ["a", "b", 0, 1, 2]

实例2:使用apply和内置函数

对于一些需要写循环以便历数组各项的需求,我们可以用apply完成以避免循环。下面是示例,我们将用Math.max/Math.min求得数组中的最大/小值。

/* 找出数组中最大/小的数字 */
var numbers = [5, 6, 2, 3, 7];

/* 使用Math.min/Math.max以及apply 函数时的代码 */
var max = Math.max.apply(null, numbers); /* 基本等同于 Math.max(numbers[0], ...) 或 Math.max(5, 6, ..) */
var min = Math.min.apply(null, numbers);

/* 对比:简单循环算法 */
max = -Infinity, min = +Infinity;

for (var i = 0; i < numbers.length; i++) {
  if (numbers[i] > max)
    max = numbers[i];
  if (numbers[i] < min)
    min = numbers[i];
}

实例3:

function Animal(name,color){
    this.name = name;
    this.color = color;
    this.type = "animal";
}

function Bird(name,color,age){
     Animal.apply(this,arguments);//此时Animal中this 指向的是windows故需要使用apply()方法将this的指向改为 Bird实例
     this.age = age; 
} 

var sparrow = new Bird("bage","yellow","1"); 
console.log("Hello,my name is "+sparrow.name+",my color is "+sparrow.color+".I am "+sparrow.age+" years old.I am a "+sparrow.type);

2.1.2 call():

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。call() 提供新的 this 值给当前调用的函数/方法。你可以使用 call 来实现继承:写一个方法,然后让另外一个新的对象来继承它(而不是在新对象中再写一次这个方法)。

语法 call([thisObj[, arg1[, arg2[, [, argN]]]]])
参数
- thisObj: 可选的。在 function 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 nullundefined 时会自动替换为指向全局对象,原始值会被包装。
- arg1, arg2, , argN可选。 指定的参数列表。
返回值 使用调用者提供的 this 值和参数调用该函数的返回值。若该方法没有返回值,则返回 undefined
备注 call 方法用于调用代表另一项目的方法。 它允许您将函数的 this 对象从初始上下文变为由 thisObj 指定的新对象。如果没有提供 thisObj 参数,则 global 对象被用作 thisObj。

实例1:使用 call 方法调用父构造函数

在一个子构造函数中,你可以通过调用父构造函数的 call 方法来实现继承,类似于 Java 中的写法。下例中,使用 Food 和 Toy 构造函数创建的对象实例都会拥有在 Product 构造函数中添加的 name 属性和 price 属性,但 category 属性是在各自的构造函数中定义的。

function Product(name, price) {
  this.name = name;
  this.price = price;
}

function Food(name, price) {
  Product.call(this, name, price);
  this.category = 'food';
}

function Toy(name, price) {
  Product.call(this, name, price);
  this.category = 'toy';
}

var cheese = new Food('feta', 5);
var fun = new Toy('robot', 40);

实例2:使用 call 方法调用匿名函数

在下例中的 for 循环体内,我们创建了一个匿名函数,然后通过调用该函数的 call 方法,将每个数组元素作为指定的 this 值执行了那个匿名函数。这个匿名函数的主要目的是给每个数组元素对象添加一个 print 方法,这个 print 方法可以打印出各元素在数组中的正确索引号。当然,这里不是必须得让数组元素作为 this 值传入那个匿名函数(普通参数就可以),目的是为了演示 call 的用法。

var animals = [
  { species: 'Lion', name: 'King' },
  { species: 'Whale', name: 'Fail' }
];

for (var i = 0; i < animals.length; i++) {
  (function(i) {
    this.print = function() {
      console.log('#' + i + ' ' + this.species
                  + ': ' + this.name);
    }
    this.print();
  }).call(animals[i], i);
}
//#0 Lion: King
//#1 Whale: Fail

实例3:使用 call 方法调用函数并且指定上下文的 ‘this’

在下面的例子中,当调用 greet 方法的时候,该方法的this值会绑定到 obj 对象。

function greet() {
  var reply = [this.animal, 'typically sleep between', this.sleepDuration].join(' ');
  console.log(reply);
}

var obj = {
  animal: 'cats', sleepDuration: '12 and 16 hours'
};

greet.call(obj);  // cats typically sleep between 12 and 16 hours

实例4:使用 call 方法调用函数并且不指定第一个参数(argument)

在下面的例子中,我们调用了 display 方法,但并没有传递它的第一个参数。如果没有传递第一个参数,this 的值将会被绑定为全局对象。

var sData = 'Wisen';

function display() {
  console.log('sData value is %s ', this.sData);
}

display.call();  // sData value is Wisen

注意:在严格模式下,this 的值将会是 undefined。见下文。

'use strict';

var sData = 'Wisen';

function display() {
  console.log('sData value is %s ', this.sData);
}

display.call(); // Cannot read the property of 'sData' of undefined

2.2 bind()方法

bind() 函数会创建一个新的绑定函数bound function,BF)。绑定函数是一个 exotic function object(怪异函数对象,ECMAScript 2015 中的术语),它包装了原函数对象。调用绑定函数通常会导致执行包装函数
绑定函数具有以下内部属性:

  • [[BoundTargetFunction]] - 包装的函数对象
  • [[BoundThis]] - 在调用包装函数时始终作为 this 值传递的值。
  • [[BoundArguments]] - 列表,在对包装函数做任何调用都会优先用列表元素填充参数列表。
  • [[Call]] - 执行与此对象关联的代码。通过函数调用表达式调用。内部方法的参数是一个this值和一个包含通过调用表达式传递给函数的参数的列表。

当调用绑定函数时,它调用 [[BoundTargetFunction]] 上的内部方法 [[Call]],就像这样 Call(boundThis, args)。其中,boundThis[[BoundThis]]args[[BoundArguments]] 加上通过函数调用传入的参数列表。
绑定函数也可以使用 new 运算符构造,它会表现为目标函数已经被构建完毕了似的。提供的 this 值会被忽略,但前置参数仍会提供给模拟函数。

| 语法 | ``` function

.bind(thisArg[, arg1[, arg2[, ...]]]) |
| --- | --- |
| **参数** | <br />- thisArg: 调用绑定函数时作为 `this` 参数传递给目标函数的值。 如果使用[`new`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/new)运算符构造绑定函数,则忽略该值。当使用 `bind` 在 `setTimeout` 中创建一个函数(作为回调提供)时,作为 `thisArg` 传递的任何原始值都将转换为 `object`。如果 `bind` 函数的参数列表为空,或者`thisArg`是`null`或`undefined`,执行作用域的 `this` 将被视为新函数的 `thisArg`<br />- arg1, arg2, , argN当目标函数被调用时,被预置入绑定函数的参数列表中的参数。<br /> |
| **返回值** | 返回一个原函数的拷贝,并拥有指定的 **`this`** 值和初始参数。 |
| **备注** | call 方法用于调用代表另一项目的方法。 它允许您将函数的 this 对象从初始上下文变为由 thisObj 指定的新对象。如果没有提供 thisObj 参数,则 global 对象被用作 thisObj。 |

<a name="zYL9v"></a>
#### 实例1:创建绑定函数
bind() 最简单的用法是创建一个函数,**不论怎么调用,这个函数都有同样的 this 值**。JavaScript新手经常犯的一个错误**是****将一个方法从对象中拿出来**,然后再调用,**期望方法中的 this 是原来的对象**(比如在回调中传入这个方法)。如果不做特殊处理的话,一般会丢失原来的对象。基于这个函数,用原始的对象创建一个绑定函数,巧妙地解决了这个问题:
```javascript
this.x = 9;    // 在浏览器中,this 指向全局的 "window" 对象
var module = {
  x: 81,
  getX: function() { return this.x; }
};

module.getX(); // 81

var retrieveX = module.getX;
retrieveX();
// 返回 9 - 因为函数是在全局作用域中调用的

// 创建一个新函数,把 'this' 绑定到 module 对象
// 新手可能会将全局变量 x 与 module 的属性 x 混淆
var boundGetX = retrieveX.bind(module);
boundGetX(); // 81

实例2:偏函数

bind() 的另一个最简单的用法是使一个函数拥有预设的初始参数。只要将这些参数(如果有的话)作为 bind() 的参数写在 this 后面。当绑定函数被调用时,这些参数会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们后面。

function list() {
  return Array.prototype.slice.call(arguments);
}

function addArguments(arg1, arg2) {
    return arg1 + arg2
}

var list1 = list(1, 2, 3); // [1, 2, 3]

var result1 = addArguments(1, 2); // 3

// 创建一个函数,它拥有预设参数列表。
var leadingThirtysevenList = list.bind(null, 37);

// 创建一个函数,它拥有预设的第一个参数
var addThirtySeven = addArguments.bind(null, 37);

var list2 = leadingThirtysevenList();
// [37]

var list3 = leadingThirtysevenList(1, 2, 3);
// [37, 1, 2, 3]

var result2 = addThirtySeven(5);
// 37 + 5 = 42

var result3 = addThirtySeven(5, 10);
// 37 + 5 = 42 ,第二个参数被忽略

实例3:配合 setTimeout

在默认情况下,使用 window.setTimeout() 时,this 关键字会指向 window (或 global)对象。当类的方法中需要 this 指向类的实例时,你可能需要显式地把 this 绑定到回调函数,就不会丢失该实例的引用。

function LateBloomer() {
  this.petalCount = Math.ceil(Math.random() * 12) + 1;
}

// 在 1 秒钟后声明 bloom
LateBloomer.prototype.bloom = function() {
  window.setTimeout(this.declare.bind(this), 1000); //显示的绑定使用boom方法的this来调用declare
};

LateBloomer.prototype.declare = function() {
  console.log('I am a beautiful flower with ' +
    this.petalCount + ' petals!');
};

var flower = new LateBloomer();
flower.bloom();  // 一秒钟后, 调用 'declare' 方法

实例4:作为构造函数使用的绑定函数

警告 :这部分演示了 JavaScript 的能力并且记录了 bind() 的超前用法。以下展示的方法并不是最佳的解决方案,且可能不应该用在任何生产环境中。
绑定函数自动适应于使用 new 操作符去构造一个由目标函数创建的新实例。当一个绑定函数是用来构建一个值的,原来提供的 this 就会被忽略。不过提供的参数列表仍然会插入到构造函数调用时的参数列表之前。

function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function() {
  return this.x + ',' + this.y;
};

var p = new Point(1, 2);
p.toString(); // '1,2'

var emptyObj = {};
var YAxisPoint = Point.bind(emptyObj, 0/*x*/);

// 本页下方的 polyfill 不支持运行这行代码,
// 但使用原生的 bind 方法运行是没问题的:

var YAxisPoint = Point.bind(null, 0/*x*/);

/*(译注:polyfill 的 bind 方法中,如果把 bind 的第一个参数加上,
即对新绑定的 this 执行 Object(this),包装为对象,
因为 Object(null) 是 {},所以也可以支持)*/

var axisPoint = new YAxisPoint(5);
axisPoint.toString(); // '0,5'

axisPoint instanceof Point; // true
axisPoint instanceof YAxisPoint; // true
new YAxisPoint(17, 42) instanceof Point; // true

实例5:快捷调用 - 没看懂

在你想要为一个需要特定的 this 值的函数创建一个捷径(shortcut)的时候,bind() 也很好用。
你可以用 Array.prototype.slice 来将一个类似于数组的对象(array-like object)转换成一个真正的数组,就拿它来举例子吧。你可以简单地这样写:

var slice = Array.prototype.slice;

// ...

slice.apply(arguments);

用 bind()可以使这个过程变得简单。在下面这段代码里面,slice 是 Function.prototype 的 apply() 方法的绑定函数,并且将 Array.prototype 的 slice() 方法作为 this 的值。这意味着我们压根儿用不着上面那个 apply()调用了。

// 与前一段代码的 "slice" 效果相同
var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.apply.bind(unboundSlice);

// ...

slice(arguments);