Function函数

173 阅读4分钟

没有重载

参数是由包含零个或多个值的数组表示的。没有函数签名,自然也就没有重载

prototype

属性存储了 Function 的原型对象

函数内部

1、arguments

属性代表传入函数的实参,它是一个类数组对象,包含调用函数时传入的所有参数。以 function 关键字定义函数(相对于使用箭头语法创建函数)时才会有。 arguments 对象其实还有一个 callee 属性,是一个指向 arguments 对象所在函数的 指针。

阶乘函数

function factorial(num) {
  if (num <= 1) {
    return 1;
  } else {
    return num * factorial(num - 1);
  }
}

阶乘函数一般定义成递归调用的,就像上面这个例子一样。只要给函数一个名称,而且这个名称不 会变,这样定义就没有问题。但是,这个函数要正确执行就必须保证函数名是 factorial ,从而导致 了紧密耦合。使用 arguments.callee 就可以让函数逻辑与函数名解耦:

function factorial(num) {
  if (num <= 1) {
    return 1;
  } else {
    return num * arguments.callee(num - 1);
  }
}

这个重写之后的 factorial() 函数已经用 arguments.callee 代替了之前硬编码的 factorial 。 这意味着无论函数叫什么名称,都可以引用正确的函数。考虑下面的情况:

let trueFactorial = factorial;
factorial = function() {
  return 0;
};
console.log(trueFactorial(5)); // 120
console.log(factorial(5)); // 0

这里, trueFactorial 变量被赋值为 factorial ,实际上把同一个函数的指针又保存到了另一个 位置。然后, factorial 函数又被重写为一个返回 0 的函数。如果像 factorial() 最初的版本那样不 使用 arguments.callee ,那么像上面这样调用 trueFactorial() 就会返回 0 。不过,通过将函数与 名称解耦, trueFactorial() 就可以正确计算阶乘,而 factorial() 则只能返回 0。

2、this

在标准函数中, this 引用的是把函数当成方法调用的上下文对象,这时候通常称其为 this 值(在网页的全局上下文中调用函数时, this 指向 windows )。

window.color = 'red';
let o = {
  color: 'blue'
};
function sayColor() {
  console.log(this.color);
}
sayColor(); // 'red'
o.sayColor = sayColor;
o.sayColor(); // 'blue'

在箭头函数中, this 引用的是定义箭头函数的上下文。下面的例子演示了这一点。在对 sayColor() 的两次调用中, this 引用的都是 window 对象,因为这个箭头函数是在 window 上下文中定义的

window.color = 'red';
let o = {
color: 'blue'
};
let sayColor = () => console.log(this.color);
sayColor(); // 'red'
o.sayColor = sayColor;
o.sayColor(); // 'red'

在事件回调或定时回调中调用某个函数时, this 值指向的并非想要的对象。此时将回调函数写成箭头函数就可以解决问题。这是因为箭头函数中的 this 会保留定义该函数时的上下文。

function King() {
  this.royaltyName = 'Henry';
  // this 引用 King 的实例
  setTimeout(() => console.log(this.royaltyName), 1000);
}
function Queen() {
  this.royaltyName = 'Elizabeth';
  // this 引用 window 对象
  setTimeout(function() { console.log(this.royaltyName); }, 1000);
}
new King(); // Henry
new Queen(); // undefined

注:函数名只是保存指针的变量。因此全局定义的 sayColor() 函数和 o.sayColor() 是同一个函数,只不过执行的上下文不同。

3、caller

ECMAScript 5 也会给函数对象上添加一个属性: caller 。虽然 ECMAScript 3 中并没有定义,但所 有浏览器除了早期版本的 Opera 都支持这个属性。这个属性引用的是调用当前函数的函数,或者如果是 在全局作用域中调用的则为 null

function outer() {
  inner();
}
function inner() {
  console.log(inner.caller);
}
outer();

以上代码会显示 outer() 函数的源代码。这是因为 ourter() 调用了 inner() , inner.caller 指向 outer() 。如果要降低耦合度,则可以通过 arguments.callee.caller 来引用同样的值

function outer() {
  inner();
}
function inner() {
  console.log(arguments.callee.caller);
}
outer();

在严格模式下访问 arguments.callee 会报错。ECMAScript 5 也定义了 arguments.caller ,但 在严格模式下访问它会报错,在非严格模式下则始终是 undefined 。这是为了分清 arguments.caller和函数的 caller 而故意为之的。而作为对这门语言的安全防护,这些改动也让第三方代码无法检测同一上下文中运行的其他代码。严格模式下还有一个限制,就是不能给函数的 caller 属性赋值,否则会导致错误。

4、new.target

ECMAScript 中的函数始终可以作为构造函数实例化一个新对象,也可以作为普通函数被调用。ECMAScript 6 新增了检测函数是否使用 new 关键字调用的 new.target 属性。如果函数是正常调用的,则 new.target 的值是 undefined ;如果是使用 new 关键字调用的,则 new.target 将引用被调用的构造函数。

function King() {
  if (!new.target) {
    throw 'King must be instantiated using "new"'
  }
  console.log('King instantiated using "new"');
}
new King(); // King instantiated using "new"
King(); // Error: King must be instantiated using "new"

length

属性指明函数的形参个数

function func1() {}

function func2(a, b) {}

console.log(func1.length);
// expected output: 0

console.log(func2.length);
// expected output: 2

apply()

Function.prototype.apply()

//apply和call的区别传参不一样
fun.apply(thisArg, [argsArray])

function getMax(arr){ return Math.max.apply(null,arr)};/*得到最大值*/
function getMin(arr){ return Math.min.apply(null,arr)};/*得到最小值*/

var arr1 = new Array('1', '2', '3');
var arr2 = new Array('4', '5', '6');

//合并数组
Array.prototype.push.apply(arr1, arr2);
console.log(arr1); //["1", "2", "3", "4", "5", "6"]

//=====
function sum(num1, num2) {
  return num1 + num2;
}
function callSum1(num1, num2) {
  return sum.apply(this, arguments); // 传入 arguments 对象
}
function callSum2(num1, num2) {
  return sum.apply(this, [num1, num2]); // 传入数组
}
console.log(callSum1(10, 10)); // 20
console.log(callSum2(10, 10)); // 20

call()

Function.prototype.call() 1.借用方法,call会修改this指向自身; 2.将伪数组改成真数组。

fun.call(thisArg, arg1, arg2, ...)
function add(a, b) {
    	return a + b;
}
function reduce(a, b) {
        return a * b;
}
add.call(reduce, 10, 20)//30



function sum(num1, num2) {
  return num1 + num2;
}
function callSum(num1, num2) {
  return sum.call(this, num1, num2);
}
console.log(callSum(10, 10)); // 20

bind()

方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。 Function.prototype.bind()

function.bind(thisArg[, arg1[, arg2[, ...]]])

const module = {
  x: 42,
  getX: function() {
    return this.x;
  }
}

const unboundGetX = module.getX;
console.log(unboundGetX()); // The function gets invoked at the global scope
// expected output: undefined

const boundGetX = unboundGetX.bind(module);
console.log(boundGetX());
// expected output: 42



window.color = 'red';
var o = {
  color: 'blue'
};
function sayColor() {
  console.log(this.color);
}
let objectSayColor = sayColor.bind(o);
objectSayColor(); // blue

toString()

方法返回一个表示当前函数源代码的字符串 Function.prototype.toString()

function sum(a, b) {
  return a + b;
}

console.log(sum.toString());//function sum(a, b) {return a + b;}

递归

一个方法自己调用自己,用上一次调用得出的结果作为这次的参数

var factorial = (function f(num) {
  return typeof num === "number" ? num <= 1 ? 1 : num + f(num - 1) : "please a number"
})

闭包(closure)

是指有权访问另一个函数作用域中的变量的函数


function MyObject(name, message) {
    this.name = name.toString();
    this.message = message.toString();
}

(function() {
    this.getName = function() {
        return this.name;
    };
    this.getMessage = function() {
        return this.message;
    };
}).call(MyObject.prototype);

原型链

//ECMAScript的所有内置对象的原型链都指向Function对象的原型
Object.__proto__ === Function.prototype //true
Function.__proto__ === Function.prototype //true
Array.__proto__ === Function.prototype //true
String.__proto__ === Function.prototype //true
Number.__proto__ === Function.prototype //true
Boolean.__proto__ === Function.prototype //true
Date.__proto__ === Function.prototype //true
Error.__proto__ === Function.prototype//true
RegExp.__proto__ === Function.prototype//true

//自定义对象的原型的原型链指向Object对象的原型
Product.prototype.__proto__ === Object.prototype //true
Function.prototype.__proto__ === Object.prototype//true
Object.prototype.__proto__ === null //true 原型链的终点

变量提升

var fn,
function name,

var fn = function(){}
name();
function name(){}