avaScript因为其语法松散,导致函数(尤其是this)看似简单,其实里面花头很多。本篇介绍一下JavaScript函数及其调用方法。
- 找资源就上去转盘(www.quzhuanpan.com)
- 函数声明和函数表达式
- arguments
- this
- this补充说明
函数声明和函数表达式
JavaScript里对象字面量产生的对象将被连接到Object.prototype,函数对象将被连接到Function.prototype(但该对象本身也连接到Object.prototype)。先看一下函数声明和函数表达式(分匿名和命名):
function count(a,b){ return a*b; } //函数声明
var d1 = function(n) { return n*2; }; //匿名函数表达式
var d2 = function double(n) { return n*2; }; //命名函数表达式
console.log(count(3,4)); //12
console.log(d1(3)); //6
console.log(d2(3)); //6
console.log(double(3)); //error,double未定义
上面代码可以看出函数声明和函数表达式在后续的调用中,效果是没有差别的。除语法不同外,两者的区别在于JS解析器读取的顺序。
解析器会事先读取函数声明,即使你把函数声明放在代码的末端也没关系。而对于函数表达式,同其它基本类型的变量一样,只有在执行到该行语句时才解析。因此用函数表达式时,必须确保它在调用语句之前,否则会报错。
再看匿名和命名函数表达式的区别。上例中命名函数表达式将函数绑定到变量d2上,而非变量double上,因此double(3);会出现未定义error。
那命名函数表达式有什么用呢?比如上面的变量double有什么用呢?函数名double可用于在函数内部做递归,但可惜仍旧没必要,因为变量d2同样也可以在函数内部递归。因此命名函数表达式真正的作用在于调试,JavaScript环境提供对Error对象的栈追踪功能,可以用double进行栈追踪。
但命名函数表达式仍旧有很多问题,类似with一样。因此通常推荐用匿名函数表达式,不推荐用命名函数表达式:
var d1 = function(n) { return n*2; }; //Yes,推荐
var d2 = function double(n) { return n*2; }; //No,不推荐
arguments
每个函数都接受2个附加参数:this和arguments。先看arguments。JS的函数参数其实就是个类似数组的arguments对象,是对形参的一个映射,但是值是通过索引来获取的。因此JS的函数天然支持可变参数。
arguments对象看似像数组,但请不要使用arguments.shift()等方法来修改arguments。修改arguments对象将可能导致命名参数失去意义。
例如person(name, age),参数name是arguments[0]的别名,age是arguments[1]的别名,如果用shift移除arguments后,name仍旧是arguments[0]的别名,age仍旧是arguments[1]的别名,函数开始失控。
因此,如果你无论如何要修改arguments,需要先将arguments对象转化为真正的数组:
var args = [].slice.call(arguments);
之后对args对象进行shift()等操作。这也常见于获取可变参数值,同样需要上述那样将arguments对象转化为真正的数组。
另外每个arguments对象都有两个额外的属性:arguments.callee和arguments.caller。前者指向使用该arguments对象被调用的函数。后者指向调用该arguments对象的函数。
其实arguments.callee除允许匿名函数递归调用自身外,并没有什么太大用处。但可惜用函数名也能实现递归,所以它真没什么用处:
//用arguments.callee来递归
var factorial = (function(n) {
return (n <= 1) ? 1 : (n * arguments.callee(n - 1)); //递归
});
//但也可以直接用函数名来递归
function factorial(n) {
return (n <= 1) ? 1 : (n * factorial(n - 1));
}
用arguments.caller可以跟踪栈信息,但它不可靠,如果某函数在栈中出现了不止一次,很容易陷入死循环,大多数环境已经移除了此特性。
JS严格模式下禁止使用arguments.callee和arguments.caller,因此这两个属性就不多废话了。
this
arguments介绍完后,再来看看this。在JS中this取决于调用的方式,不同的函数调用方式,this绑定的对象也不同。有4种调用方式:
- 方法调用
- 函数调用
- 构造器调用
- apply / call / bind调用
方法调用:当函数作为对象方法时,函数里的this被绑定到该对象上
var myNum = {
value: 0,
increment: function(inc) { //函数作为对象方法
this.value += inc;
}
};
myNum.increment(2);
console.log(myNum.value); //2,this被绑定到myNum
函数调用:函数非对象方法时,this被绑定到全局对象window。这其实是语言设计上的一个错误(或曰特性),导致this不能调用内部函数。要调用内部函数,可以将that = this保存起来。
function double(n){ return n*2; } //普通函数,this绑定到全局对象window
//错误的例子
myNum.count = function() {
var helper = function() {
this.value = double(this.value);
};
helper();
}
myNum.count();
console.log(myNum.value); //value不变
//正确的例子:
myNum.count = function() {
var that = this;
var helper = function() {
that.value = double(that.value); //现在参数是myNum.value
};
helper();
}
myNum.count();
console.log(myNum.value); //4
错误的例子中,期望this绑定的是对象myNum,但由于double是普通函数,因此this绑定的是window,而window显然没有value。即helper里this是window,因此double(this.value);不会被执行。最终myNum的value值并没有变。
正确的例子在对象myNum方法里,this绑定的是myNum对象,因此先用that将this保存起来。然后在内部传递的都是that,回避了helper函数内this发生改变的问题。
构造函数调用:用new调用构造函数,会先创建一个连接到构造函数的prototype的新对象,再将this会绑定到该新对象
var Name = function(n) {
this.name = n;
}
Name.prototype.getName = function() {
return this.name;
}
var myName = new Name("Jack"); //this绑定到myName对象
console.log(myName.getName()); //Jack
apply / call / bind调用:允许我们自己绑定想要的this
var friend = {
name: "Betty"
};
console.log(Name.prototype.getName.apply(friend)); //Betty
console.log(Name.prototype.getName.call(friend)); //Betty
console.log(Name.prototype.getName.bind(friend)()); //Betty
this补充说明
这一节并无任何新的内容,只不过对this进一步补充说明一下。我们知道对象都有prototype俗称原型对象。那prototype里的this绑定谁呢?其实原则没有变,从上面构造函数调用的例子就能看出this仍旧是绑定调用的对象。
为了更清晰一点,将上面构造函数调用的例子稍微改一下:
var Name = function() {};
Name.prototype = {
name: "(not set)",
setName: function(n) {
this.name = n;
}
}
var myName = new Name();
console.log(myName.name); //(not set)
console.log(myName.hasOwnProperty("name")); //false
console.log(myName.hasOwnProperty("setName")); //false
myName.setName("Jack");
console.log(myName.name); //Jack
console.log(myName.hasOwnProperty("name")); //true
console.log(myName.hasOwnProperty("setName")); //false
先看第一段结果代码,Name本身没有任何属性,name和setName是在它的原型prototype中定义的。因此用hasOwnProperty来检查全是false。这与们的预想完全一致,没什么可奇怪的。
再看第二段结果代码,由于执行了myName.setName("Jack");。原型prototype中的this不是绑定原型对象,而是绑定调用的对象。即setName中的this绑定的是对象myName,会给对象增加一个name属性。所以hasOwnProperty("name")会为true。
明白这些原理后,再回过头看看以前不明白的代码里this,that,self等就轻松多了。
self补充说明
这个非常简单。我们知道,打开任何一个网页,浏览器会首先创建一个窗口,这个窗口就是一个window对象,也是js运行所依附的全局环境对象和全局作用域对象。self 指窗口本身,它返回的对象跟window对象是一模一样的。也正因为如此,window对象的常用方法和函数都可以用self代替window。举个例子,常见的写法如“self.close();”,把它放在标记中:“关闭窗口”,单击“关闭窗口”链接,当前页面关闭。
明白这些原理后,再回过头看看以前不明白的代码里this,that,self等就轻松多了。
相关推荐
JavaScript函数是编程语言的核心组成部分,它是一段可重复使用的代码块,可以接受参数并返回值。在JavaScript中,函数不仅可以作为表达式,还能作为变量赋值、作为参数传递以及作为返回值。本速查指南将深入探讨...
以上是对JavaScript函数的深入探讨,理解这些知识点对于编写高效、灵活的JavaScript代码至关重要。在实际开发中,应根据需求选择合适的函数定义方式,并充分利用JavaScript提供的各种调用机制和特性。
理解JavaScript中的arguments和this对象对于编写有效的代码是非常重要的。掌握它们的用法可以帮助开发者避免常见的错误,并能够更灵活地处理函数参数和上下文环境。在实际开发中,我们应当充分了解和利用这些特性来...
以下是关于JavaScript函数的一些关键知识点: 1. **函数定义**: - **函数声明**:通过`function`关键字定义函数,例如`function content() { console.log("水果"); }`。函数声明在代码加载时会被提升,允许在声明...
JavaScript的函数劫持是一种高级编程技巧,主要用于调试、性能分析或者在不修改原始代码的情况下扩展或改变既有功能...但是,理解了上述函数劫持的概念和实现方式,你应该能更好地理解和应用相关的JavaScript编程技巧。
标题 "解析 this.initialize.apply(this, arguments)" 涉及到的是JavaScript编程中的一个关键概念,尤其是在对象构造和类继承的情景下。`this` 关键字在JavaScript中扮演着核心角色,它指的是函数调用时的上下文,而...
JavaScript中的箭头函数是ES6引入的一个重要特性,它的出现主要解决了传统函数中`this`指针的问题。在JavaScript中,`this`的...在编写JavaScript代码时,理解并正确使用箭头函数对于提高代码质量和可维护性至关重要。
JavaScript函数柯里化是一种编程技巧,它源自于数学家哈斯凯尔·加里(Haskell Curry)的名字,因此称为“柯里化”。这种技术的主要目的是将一个多参数的函数转化为一系列单参数的函数,使得每次调用只处理一个参数,...
JavaScript中的箭头函数和普通函数...了解这些区别对于编写高效、可维护的JavaScript代码至关重要,尤其是在处理`this`、构造函数、继承和异步操作时。在选择使用箭头函数还是普通函数时,应根据实际需求和功能来决定。
JavaScript函数手册是一本详细阐述JavaScript函数使用和理解的资源,主要以HTML格式提供,适合英文阅读者学习。手册涵盖了从基础到高级的各种函数用法,是开发者深入理解JavaScript语言的重要参考资料。通过各个部分...
JavaScript中的call、apply和bind方法都是用来改变函数调用时的上下文(即this值)以及传递参数。它们之间的相同点在于,都能够指定函数执行时的this对象,并且都能接收参数。不同点在于它们的调用方式和执行时机。 ...
这两种函数在语法和行为上都有所不同,对于理解和编写JavaScript代码至关重要。 ### 普通函数 #### 1. 定义 普通函数通过`function`关键字定义,如: ```javascript function myFunction(param1, param2) { // ...
在JavaScript编程中,函数是一个核心概念,而`arguments`参数是函数对象的一个内置属性,它提供了一种方式来访问函数内部被传递的所有参数。无论函数是否声明了参数列表,`arguments`对象都可以在函数体内部被访问。...
通过理解并熟练运用这些函数相关的特性,开发者可以写出更高效、更易于维护的JavaScript代码。在实际项目中,比如LAMP兄弟连的PHP和JavaScript教程中,这些知识是构建复杂Web应用程序的基础。通过观看`4_JavaScript...
在JavaScript编程语言中,`arguments`对象是一个非常特殊且实用的特性,它是每个函数内部的一个内置属性,用于存储传入函数的所有参数。即使函数声明时没有定义形参,`arguments`对象仍然会存在,它提供了访问函数...
JavaScript中的函数是编程的核心组成部分,它们允许我们将一系列代码组织在一起,以便在需要时重复使用,降低了代码的复杂性。...理解并熟练运用函数的各种特性,是成为一名优秀的JavaScript开发者的关键。
总结而言,arguments和this是JavaScript函数内部非常重要的两个属性。arguments对象帮助我们访问函数参数,而this对象则决定了函数执行时的上下文环境。了解并熟练运用这两个属性,能够帮助我们写出更加灵活和强大的...
JavaScript函数大全是对JavaScript编程语言中各种函数的详尽概述,涵盖了从基础到高级的各种功能。JavaScript,作为一种广泛应用于网页和互联网应用的脚本语言,它的函数是实现动态交互和复杂逻辑的核心工具。以下是...
JavaScript中的内置对象arguments是一个特别的对象,它是一个类数组对象,存在于所有JavaScript函数中。它主要用来存储传递给函数的参数,这些参数被称为函数的“实参”。虽然arguments对象包含了传递给函数的所有...