Open felix-cao opened 6 years ago
在上一篇《JavaScript 函数的三种定义方式》, 我们提到在 JavaScript 语言里, 函数也是对象, 函数既然是对象就一定有对应的属性和方法, 本篇来讲讲函数的属性和方法。
JavaScript
在函数内部有两个特殊的属性: arguments 和 this
arguments
this
Array
它引用着函数的实参,可以用数组下标的方式 [] 引用 arguments 的元素。arguments.length 为函数实参个数,arguments.callee 引用函数自身。
[]
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
阶乘函数(Factorial):使用arguments.callee与函数名解耦
function factorial (num){ if(num <= 1){ return 1; } else{ return num * arguments.callee(num-1);; } }
重写后的 factorial() 函数的函数体内,没有再引用函数名 factorial。这样即使改变函数名字,都可以保证正常完成递归调用.
factorial()
JavaScript 中的 this 总是指向一个对象,而具体指向哪个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境。
请移步 《JavaScript 中的 this 绑定/指向》
在函数内部属性部分我们讲到 arguments 对象的 length 属性表示实参个数,而函数本身的 length属性则表示形参个数
length
function person(name, age) { console.log('实参个数:', arguments.length); console.log('形参个数:', person.length); } person('up8', 2, '127.0.0.1');
函数定义了一个非标准的 name 属性,通过这个属性可以访问到给定函数指定的名字,这个属性的值永远等于跟在 function 关键字后面的标识符,匿名函数的 name 属性为空
name
function
// 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'
[注意]
这里的代码,把第一行移到最后一行,其结果不变,为什么? 请参考《JavaScript 作用域》 4.3部分
name 属性早就被浏览器广泛支持,但是直到 ES6 才将其写入了标准,ES6 对这个属性的行为做出了一些修改。如果将一个匿名函数赋值给一个变量,ES5 的 name 属性,会返回空字符串,而ES6 的 name 属性会返回实际的函数名
ES6
ES5
var func1 = function () {}; func1.name // ES5: ""
如果将一个具名函数赋值给一个变量,则 ES5 和 ES6 的 name 属性都返回这个具名函数原本的名字
var bar = function baz() {}; bar.name // ES5: "baz" bar.name // ES6: "baz"
Function 构造函数返回的函数实例,name 属性的值为 anonymous
Function
anonymous
(new Function).name // "anonymous"
bind 返回的函数,name 属性值会加上 bound 前缀
bind
bound
function foo() {}; foo.bind({}).name // "bound foo"
每一个函数都有一个 prototype 属性,这个属性指向一个对象的引用,这个对象称做原型对象(prototype object)。每一个函数都包含不同的原型对象。将函数用做构造函数时,新创建的对象会从原型对象上继承属性. 关于 prototype 属性请移步 《JavaScript 原型及原型对象》
prototype
(prototype object)
每个函数都有 call(), apply(), bind() 三个方法,他们来源于 Function.prototype.call(),Function.prototype.apply(), Function.prototype.bind()。
call()
apply()
bind()
Function.prototype.call()
Function.prototype.apply()
Function.prototype.bind()
上文提到,在 JavaScript 语言里, 函数也是对象,每个函数都是 Function 类的实例,而 call、apply 和 bind 是 Function 类自带的三个方法。 想了解更多请移步《JavaScript函数的call/apply/bind方法》
call
apply
函数的 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中也提到过类似的情况。
但实际上,这种写法在实际的项目中是没有任何意义的,因为其属性和方法不被实例对象所继承。 这里有一道面试题,帮助理解一下.
在上一篇《JavaScript 函数的三种定义方式》, 我们提到在
JavaScript
语言里, 函数也是对象, 函数既然是对象就一定有对应的属性和方法, 本篇来讲讲函数的属性和方法。一、 函数的内部属性
在函数内部有两个特殊的属性:
arguments
和this
1.1、arguments 对象
JavaScript
并没有重载函数的功能, 但是arguments
对象能够模拟重载。arguments
对象是所有(非箭头)函数中都可用的局部变量。你可以使用arguments
对象在函数中引用函数的参数。arguments
对象是一个类数组对象(不是一个Array
,类似于Array
,除了长度之外没有任何Array
属性。)。它引用着函数的实参,可以用数组下标的方式
[]
引用arguments
的元素。arguments.length
为函数实参个数,arguments.callee
引用函数自身。使用类数组下标:
阶乘函数(Factorial):递归调用
定义阶乘函数一般都会用到递归算法,如上面代码所示,在有函数名字,并且函数名字以后也不会改变的情况下,这种定义是没有问题的,但是这个函数的执行与函数名
factorial
紧紧的耦合在一起,为了消除这种紧密耦合现象,可以使用arguments.callee
阶乘函数(Factorial):使用
arguments.callee
与函数名解耦重写后的
factorial()
函数的函数体内,没有再引用函数名factorial
。这样即使改变函数名字,都可以保证正常完成递归调用.1.2、 this
JavaScript
中的this
总是指向一个对象,而具体指向哪个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境。请移步 《JavaScript 中的 this 绑定/指向》
二、函数的属性
2.1、length 属性
在函数内部属性部分我们讲到
arguments
对象的length
属性表示实参个数,而函数本身的length
属性则表示形参个数2.2、 name 属性
函数定义了一个非标准的
name
属性,通过这个属性可以访问到给定函数指定的名字,这个属性的值永远等于跟在function
关键字后面的标识符,匿名函数的name
属性为空[注意]
这里的代码,把第一行移到最后一行,其结果不变,为什么? 请参考《JavaScript 作用域》 4.3部分
name
属性早就被浏览器广泛支持,但是直到ES6
才将其写入了标准,ES6
对这个属性的行为做出了一些修改。如果将一个匿名函数赋值给一个变量,ES5
的name
属性,会返回空字符串,而ES6
的name
属性会返回实际的函数名如果将一个具名函数赋值给一个变量,则
ES5
和ES6
的name
属性都返回这个具名函数原本的名字Function
构造函数返回的函数实例,name
属性的值为anonymous
bind
返回的函数,name
属性值会加上bound
前缀2.3、prototype 属性
每一个函数都有一个
prototype
属性,这个属性指向一个对象的引用,这个对象称做原型对象(prototype object)
。每一个函数都包含不同的原型对象。将函数用做构造函数时,新创建的对象会从原型对象上继承属性. 关于prototype
属性请移步 《JavaScript 原型及原型对象》三、函数的方法
每个函数都有
call()
,apply()
,bind()
三个方法,他们来源于Function.prototype.call()
,Function.prototype.apply()
,Function.prototype.bind()
。上文提到,在
JavaScript
语言里, 函数也是对象,每个函数都是Function
类的实例,而call
、apply
和bind
是Function
类自带的三个方法。 想了解更多请移步《JavaScript函数的call/apply/bind方法》3.1、toString 方法
函数的
toString
方法返回一个字符串,内容是函数的源码。四、函数本身的自定义属性和方法
既然函数也是对象,那么对象的属性和方法的添加非常简单灵活:
注意,这种给函数本身添加自定义属性和方法的行为,是不被实例对象所继承的
同样的案例在 《JavaScript 立即执行函数 IIFE》中,4.3中也提到过类似的情况。
但实际上,这种写法在实际的项目中是没有任何意义的,因为其属性和方法不被实例对象所继承。 这里有一道面试题,帮助理解一下.
Rerfence