函数
js中最关键的概念:函数是第一类对象,函数与对象共存,都可以实现
· 通过字面量创建
· 赋值给变量、数组或其他对象
· 作为函数的参数
· 动态创建及分配
回调函数:在随后某个时间点会“回过来调用”的函数。
简单的回调函数:js能够对数组使用比较器实现排序,只需要调用array.sort方法
var arrays = [0, 3, 2, 4, 7, 9, 1];
alert(arrays.sort(function (value1, value2) {
return value1 - value2;
}))
如上简单的排序算法实现升序排列,若实现降序排列,只需更改为value2-value1。基本原理为:通过使用value1-value2确定是否为正数,确定是否需要调换值,内置了比较函数作为回调。
自记忆函数
将一个函数的计算结果存储起来,另一个调用也使用相同参数时,直接返回结果。
function isPrime(value) {
if (!isPrime.answers) {
//检查answer确定是否创建一个cache
isPrime.answers = {};
}
if (isPrime.answers[value] !== undefined) {
//若cache中已存在,直接返回值
return isPrime.answers[value];
}
//校验素数
var prime = value !== 1;
for (var i = 2; i < value; i++) {
if (value % i === 0) {
prime = false;
break;
}
}
//添加cache
return isPrime.answers[value] = prime;
}
//第一次调用
assert(isPrime(5), "5 is prime!");
//第二次调用
assert(isPrime.answers[5], "The answer was cached!");
优点:函数调用时会自动寻找之前调用存储的值,提高处理效率
缺点:任何类型的缓存都必然会为性能牺牲缓存,并且很难做负载测试和估计算法复杂度
函数声明
function funcName(arg1,arg2){statement;}
函数表达式
funcName(1,2);
立即函数
(function(){})(1,2) -必须包含外部(),外部()标识该表达式为立即调用,否则会被认为函数声明,引发异常——被当做函数声明但是没有函数名,函数声明的函数名是必须的。
箭头函数 -即lambda函数,ES6新增,用于简化函数创建方式
param => expression 单参数单表达式格式
(param1,param2...) =>{ expression1;expression2;...} 多参数代码块格式
arrays.sort((value1,value2) => value1 - value2 );
若函数体是代码块,若没有return语句,返回值为undefined!!!
关于剩余参数
function test(first,second,...remainingParams){}
剩余参数使用...作为前缀,可以为一个也可为多个,只有函数的最后一个参数才能为剩余参数
默认参数
function test(param, defaultParam ='defaultParamVal'){}
默认参数可以为参数defaultParam设置默认值,如果指定实参的值,默认值会被覆盖。
隐式参数-arguments、this
arguments一般用于函数内,表示传递给函数的所有参数的集合--实参集合。
function test(a,b,c){ assert(arguments.length === 5," 5 arguments" )};
test(1,2,3,4,5);
此时,arguments表示{1,2,3,4,5} 实参集合,而非等于形参的数量。arguments对象是函数参数的别名,所以如果改变了arguments的值,也会影响对应的函数参数。
什么是严格模式?
ES5启用,可以改变js引擎的默认行为并执行更加严格的语法检查,arguments别名的情况在严格模式下无法使用。
"use strict"; ---开启严格模式
为什么this参数表示函数上下文?
this一般指定义当前方法的类的实例。this的指向不仅是由定义函数的方式和位置决定的,也收到函数调用方式的影响。非严格模式下,this为全局上下文对象(window对象),严格模式下为undefined
调用一个函数有哪些方式?
1. 直接调用 -skulk();
除另外三种方法调用函数之外,都称为直接调用。
例如:
function ninja(){};
ninja(); //函数定义 直接调用
var test = function(){};
test(); //作为表达式 直接调用
(function(){})(); //作为立即调用函数 直接调用
2.作为方法调用 -ninja.skulk();
function whatsMyContext() {
return this;
}
var ninja1 = {
getMyThis: whatsMyContext //面向对象,使用ninja1对象方法getMyThis调用whatsMyContext
};
正常使用whatsMyContext()函数直接调用,在非严格模式下,得到的this是window对象,而当getMyThis属性调用whatsMyContext函数,函数的上下文变成了ninja1,总结:函数的上下文取决于其调用方式。
3.作为构造函数调用 -new Ninja();
var puppet = {
rules: false
};
function Emperor() {
this.rules = true;
return puppet;
}
new Emperor().rules === false,构造函数中对函数的上下文操作是无效的。
4.通过apply或call方法调用 -skulk.apply(ninja); or skulk.call(ninja);
func.apply(obj,[param1,param2,...]); //apply传递参数数组
func.call(obj,param1,param2,...); //call传递参数列表
箭头函数绕过上下文
箭头函数没有单独的this值,箭头函数的this值与所声明的上下文相同。
bind方法绑定上下文
var button = {
clicked: false,
click: function(){
this.clicked = true;
assert(button.clicked,"The button has been clicked");
}
};
var elem = document.getElementById("test");
elem.addEventListener("click", button.click.bind(button));
所有函数都可以访问bind方法,可以创建并返回一个新函数,并绑定在传入的对象上,不管如何调用函数,this都是对象本身。
函数和方法之间有什么区别?
声明不同、结构不同
如果一个构造函数显式地返回一个对象会发生什么?
var puppet = {
rules: false
};
function Emperor() {
this.rules = true;
return puppet;
}
var emperor = new Emperor();
assert(emperor === puppet,
"The emperor is merely a puppet!");
assert(emperor.rules === false,
"The puppet does not know how to rule!");
新生成的对象会传递给构造函数作为函数上下文this,同时被初始化。
总结:
如果构造函数返回一个对象,则该对象作为整个表达式的值返回,而传入构造函数的this将会被丢弃;
但是,如果构造函数返回的是非对象类型,则忽略返回值,返回新创建的对象。