js函数小结

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhaostrong/article/details/86011901

js函数小结

js函数的定义

function functionName(parameters) {
  执行的代码
}
  • 示例如下:
function myFunction(a, b) {
    return a * b;
}
  • 在函数表达式存储在变量后,变量也可作为一个函数使用:
  • var x = function (a, b) {return a * b};
  • var z = x(4, 3)
  • 函数其实在js中是对象,既然是对象也就有构造函数,那么创建函数的方式又多了一种如下:
  • var myFunction = new Function("a", "b", "return a * b");
  • var x = myFunction(4, 3);

函数的提升

  • 在js中可以先使用后定义,其实在java中就是不可以的,在js中为什么可以呢?原因是提升(Hoisting)是 JavaScript 默认将当前作用域提升到前面去的的行为
myFunction(5);

function myFunction(y) {
    return y * y;
}
  • 对于函数表达式也是一样的。可以先调用后定义如下:
var z = x(4, 3);
var x = function (a, b) {return a * b};
  • 函数声明与函数表达式
    • 其实函数声明和函数表达式是有区别的。实际上解析器在向执行环境中加载数据时,对函数声明和函数表达式并非一视同仁。解析器会率先读取函数声明,并使其在执行任何代码之前可用,至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解释执行。请看下面的例子:
alert(sum(10,10));

function sum(num1,num2){
	return num1+num2;
}

函数是对象

在 JavaScript 中使用 typeof 操作符判断函数类型将返回 “function” 。
但是JavaScript 函数描述为一个对象更加准确。

JavaScript 函数有属性和方法。

  • 每个函数都有包含俩个属性:lenght和prototypoe。其中,lenght属性表示函数希望接收的命名参数的个数,如下:
function myFunction(a, b) {
    return arguments.length;
}

函数作为值来用

  • 如下:
function myFunction(a, b) {
    return a * b;
}

var x = myFunction(4, 3);
function myFunction(a, b) {
    return a * b;
}

var x = myFunction(4, 3) * 2;

js没有重载(深入理解)

  • 将函数名想象为指针,也有助于理解为什么ECMAScript中没有函数虫灾的概念。
function addSomeNumber(num){
		return num+100;
}
function addSomeNumber(num){
		return num+200;
}
var result=addSomeNumber(100);//300

显然,这个例子中声明了俩个同名函数,而结果则是后面的函数覆盖了前面的函数。以上代码实际上与下面的代码没有什么区别。

函数内部属性

  • 在函数内部,有俩个特殊的对象:arguments 和this 。其中,argumengs它是一个类数组对象,包含着出入函数中的所有参数。虽然arguments 的主要用途是保存函数参数,但这个对象还有一个名叫callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。
		function factorial(num){
			if(num<=1){
				return 1;
			}else{
				return num*factorial(num-1);
			}
		}
  • 定义阶乘函数一般都要用到递归算法,如上面的代码所示,在函数有名字,而且名字以后也不会变的情况下,这样定义没有问题。但问题是这个函数的执行与函数名factorial紧紧耦合在一起了。为了消除这中紧密耦合的现象,可以像下面这样使用 argumens.callee。
function factorial(num){
		if(num<=1){
			return 1;
		}else{
			return num*arguments.callee(num-1);
		}
}
  • 在这个重写后的factorial()函数的函数体内,没有在引用函数名factorial。这样,无论引用函数时用的是什么名字,都可以保证正常完成递归调用。例如:
var trueFactorial=factorial;
factorial =function(){
	return 0;
};
alert(trueFactorial(5));//120
alert(factorial(5)); //0
  • 在此,变量trueFactorial获得了factorial的值,实际上是在另一个位置上保存了一个函数的指针。然后我们又将一个简单地返回0的函数赋值给factorial变量。如果像原来的factorial()那样不使用arguments.callee,调用trueFactorial()就会返回0.可是在解除了函数体内的代码与函数名的耦合状态后,trueFactorial()仍然能够正常地计算阶乘;至于factorila(),它现在只是一个返回0 的函数。
  • 每个函数都包含俩个非继承而来的方法:apply()和call().这来个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。首先,apply()方法接收俩个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是Array的实例,也可以是arguments对象。例如:
function sum(num1,num2){
		return num1+num2;
}
function callSum1(num1,num2){
	return sum.apply(this,arguemnts);//传入arguments参数
}
function callSum2(num1,num2){
	return sum.apply(this,[num1,num2]);//传入数组
}

alert(callSum1(10,10));//20
alert(callSum2(10,10));//20
  • 在上面这个例子中,callSum1()在执行sum()函数时传入了this 作为this值(因为是在全局作用域中调用的,所以传入的对象就是window对象)和arguments对象。而calSum2同样也调用了sum()函数,但是它传入的则是this和一个参数数组。这来个函数都会正常执行并返回正确的结果。
  • 在严格模式下,为指定环境对象而调用函数,则this值不会转型为window。除非明确把函数添加到某个对象或者调用apply()或者call(),否则this值将是undefined。
  • call()方法与apply()方法的作用相同,它们的区别仅在于即接收参数的方式不同。对于call()方法而言,第一个参数是this值没有变化,变化的是其余参数都直接传递给函数。换句话说,在使用call方法时,传递给函数的参数必须逐个列举出来,如下:
function sum(num1,num2){
	return num1+num2;
}
function callSum(num1,num2){
	return sum.call(this,num1,num2);
}

alert(callSum(10,10));//20
  • 在使用call方法的情况下,callSum()必须明确地传入每一个参数。结果与使用apply()没有什么不同。至于是使用apply()还是call(),完全取决于你才用哪种给函数传递参数的方式最方便。如果你打算直接传入arguments对象,或者包含函数中先接收到的也是一个数组,那么使用apply()肯定更方便;否则,选择call()可能更合适。
  • 事实上,传递参数并非apply()和call()真正的用武之地;它们真正强大的地方是能够扩充函数赖以运行的作用域。来看一个例子:
window.color="red";
var o={color:"blue"};
function sayColor(){
	alert(this.color);
}
sayColor(); //red
sayColor.call(this); //red
sayColor.call(window);  //red
sayColor.call(o); //blue

  • 这个例子是在前面说明this对象的示例基础上修改的。这一次,sayColor()也是作为全局函数定义的,而且当在全局作用域中调用它时,它确实会显示“red" 因为对this.color的求值会转换成对window.color的求值。而sayColor.call(this)和sayColor.call(window),则是俩个新鲜事地在全局作用域中调用函数的方式,结果当前都会显示“red".但是,当运行sayColor.call(o);时,函数的执行环境就不一样了,因为此时函数体内的this指向了o,于是结果显示的是“blue".
  • 使用call()或者apply() 来扩充作用域的最大好处,就是对象不需要与方法有任何耦合关系。在前面例子的第一个版本中,我们是先将sayColor()函数放到了对象o中,然后再通过o来调用它的;而在这里重写的例子中,就不需要先前那个多余的步骤了。
  • ECMAScript5还定义了一个方法:bind()。这个方法会创建一个函数的实例,其this值会被绑定到传给bind()函数的值。例如:
window.color ="red";
var o={color: "blue"};
function sayColor(){
	alert(this.color);
}
var objectSayColor=sayColor.bind(o);
objectSayColor();//blue
  • 在这里,sayColor调用bind()并传入对象o,创建了objectSayColor()函数。objectSayColor()函数的this值等于o,因此即使是在全局作用域中调用这个函数,也会看到“blue"。

备注:参考了js红宝书

猜你喜欢

转载自blog.csdn.net/zhaostrong/article/details/86011901
今日推荐