felix-cao / Blog

A little progress a day makes you a big success!
31 stars 4 forks source link

JavaScript 函数的属性和方法 #46

Open felix-cao opened 6 years ago

felix-cao commented 6 years ago

在上一篇《JavaScript 函数的三种定义方式》, 我们提到在 JavaScript 语言里, 函数也是对象, 函数既然是对象就一定有对应的属性和方法, 本篇来讲讲函数的属性和方法。

一、 函数的内部属性

在函数内部有两个特殊的属性: argumentsthis

1.1、arguments 对象

它引用着函数的实参,可以用数组下标的方式 [] 引用 arguments 的元素。arguments.length 为函数实参个数,arguments.callee 引用函数自身。

使用类数组下标:

person('heeloo', 'word', 'up8', 'Felix Cao');
function person() {
  for(var i = 0; i < arguments.length; i++) {
    console.log('parameter ' + i + ': ', arguments[i]);
  }
}

阶乘函数(Factorial):递归调用

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

定义阶乘函数一般都会用到递归算法,如上面代码所示,在有函数名字,并且函数名字以后也不会改变的情况下,这种定义是没有问题的,但是这个函数的执行与函数名 factorial 紧紧的耦合在一起,为了消除这种紧密耦合现象,可以使用 arguments.callee

阶乘函数(Factorial):使用arguments.callee与函数名解耦

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

重写后的 factorial() 函数的函数体内,没有再引用函数名 factorial。这样即使改变函数名字,都可以保证正常完成递归调用.

1.2、 this

JavaScript 中的 this 总是指向一个对象,而具体指向哪个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境。

请移步 《JavaScript 中的 this 绑定/指向》

二、函数的属性

2.1、length 属性

在函数内部属性部分我们讲到 arguments 对象的 length 属性表示实参个数,而函数本身的 length属性则表示形参个数

function person(name, age) {
  console.log('实参个数:', arguments.length);
  console.log('形参个数:', person.length);
}
person('up8', 2, '127.0.0.1');

2.2、 name 属性

函数定义了一个非标准的 name 属性,通过这个属性可以访问到给定函数指定的名字,这个属性的值永远等于跟在 function 关键字后面的标识符,匿名函数的 name 属性为空

// IE11-浏览器无效,均输出undefined
// chrome在处理匿名函数的name属性时有问题,会显示函数表达式的名字
function fn() {};
console.log(fn.name); // 'fn'
var fn = function() {};
console.log(fn.name); // 在chrome浏览器中会显示'fn'
var fn = function abc() {};
console.log(fn.name); // 'abc'

[注意]

2.3、prototype 属性

每一个函数都有一个 prototype 属性,这个属性指向一个对象的引用,这个对象称做原型对象(prototype object)。每一个函数都包含不同的原型对象。将函数用做构造函数时,新创建的对象会从原型对象上继承属性. 关于 prototype 属性请移步 《JavaScript 原型及原型对象》

三、函数的方法

每个函数都有 call(), apply(), bind() 三个方法,他们来源于 Function.prototype.call()Function.prototype.apply(), Function.prototype.bind()

上文提到,在 JavaScript 语言里, 函数也是对象,每个函数都是 Function 类的实例,而 callapplybindFunction 类自带的三个方法。 想了解更多请移步《JavaScript函数的call/apply/bind方法》

3.1、toString 方法

函数的 toString 方法返回一个字符串,内容是函数的源码。

function add(x,y) {
  return x+y;
}
add.toString()

四、函数本身的自定义属性和方法

既然函数也是对象,那么对象的属性和方法的添加非常简单灵活:

function Person() {

}

Person.from = 'Felix';
Person.getFrom = function() {
  console.log('in getFrom: ', Person.from );
}
console.log(Person.from); // Felix
console.log(Person.getFrom()); // in getFrom: Felix

注意,这种给函数本身添加自定义属性和方法的行为,是不被实例对象所继承的

function Person() {

}

Person.from = 'Felix';
Person.getFrom = function() {
  console.log('in getFrom: ', Person.from );
}
const person_ = new Person();
console.log(person_.from); // undifined
console.log(person_.getFrom()); // Uncaught TypeError: person_.getFrom is not a function

同样的案例在 《JavaScript 立即执行函数 IIFE》中,4.3中也提到过类似的情况。

但实际上,这种写法在实际的项目中是没有任何意义的,因为其属性和方法不被实例对象所继承。 这里有一道面试题,帮助理解一下.

Rerfence