Javascript设计得最出色的就是他的函数的实现。 函数就是对象,唯一与众不同的是它可以被调用。(没有重载)
function 函数名(形式参数) {
函数功能实现
}
var 函数名 = function [函数名](形式参数) {
函数功能实现
}
此处函数名可省略,省略后被称为匿名函数表达式。
区分函数声明和表达式最简单的方法是看 function 关键字出现在声明中的位置.如果 function 是声明中 的第一个词,那么就是一个函数声明,否则就是一个函数表达式
函数声明和函数表达式之间最重要的区别是它们的名称标识符将会绑定在何处。函数表达式的标识符只有在函数内部才能访问。
尽量少使用匿名函数表达式。匿名函数在栈追踪中不会显示出有意义的函数名。函数名是对函数的自解释
var 函数名 = new Function(形式参数, 函数体);
传入值都是字符串类型,最后一个参数始终被看成函数体。 不推荐使用:
javascript引擎在执行前的解析阶段有一个“函数声明提升”的过程。把函数声明提升到作用域的顶部。
当然变量声明也会提升,但函数声明具有比变量声明更高的优先级。
(function(){
var test;
function test() {console.log('test')};。
console.log(typeof test); // function
})();
(function(){
function test() {console.log('test')};
var test;
console.log(typeof test); // function
})()
除了函数声明,后面两种定义方式都不会有提升效果
ES6之前js只有函数作用域,没有块作用域。ES6之前函数作用域是最常见的作用域单元,ES3开始就有块级作用域。
with会创建块级作用域
try/catch 的 catch 分句会创建一个块作用域,其中声明的变量仅在 catch 内部有效
try {
throw undefined;
} catch (a) {
a = 2;
console.log( a );
}
console.log( a );
闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。 函数定义内部可以自由访问把它嵌套在其中的父函数的参数与变量。
arguments是个类数组对象(拥有数值索引和length属性),包含这传入函数中的所有参数。
同时还有一个callee属性,是一个指向拥有这个arguments对象的函数。 在 ECMAScript 第五版 (ES5) 的 严格模式 中禁止使用 arguments.callee()。
使用arguments.callee.caller代替
在函数内部,this的取值取决于函数是如何调用的。
DOM事件处理函数中的 this
当函数被用作事件处理函数时,它的this指向触发事件的元素
// 被调用时,将关联的元素变成蓝色
function bluify(e){
console.log(this === e.currentTarget); // 总是 true
// 当 currentTarget 和 target 是同一个对象是为 true
console.log(this === e.target);
this.style.backgroundColor = '#A5D9F3';
}
// 获取文档中的所有元素的列表
var elements = document.getElementsByTagName('*');
// 将bluify作为元素的点击监听函数,当元素被点击时,就会变成蓝色
for(var i=0 ; i<elements.length ; i++){
elements[i].addEventListener('click', bluify, false);
}
内联事件处理函数中的 this
当代码被内联处理函数调用时,它的this指向监听器所在的DOM元素
<button onclick="alert(this.tagName.toLowerCase());"></button>
函数是否在new中调用(new绑定)?如果是的话this绑定的是新创建的对象。
function foo() {
console.log(this.a);
}
var o= {a: 3};
var fooBind = foo.bind(o)
fooBind(); // 3
new fooBind(); // undefined
函数是否通过call、apply(显式绑定)或者bind(硬绑定)调用?如果是的话,this绑定的是 指定的对象。
function foo() {
console.log(this.a);
}
var o= {a: 3};
var fooBind = foo.bind(o)
fooBind(); // 3
fooBind.call({a: 4}); // 3
函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定的是那个上 下文对象。
如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到 全局对象。
例外
如果你把 null 或者 undefined 作为 this 的绑定对象传入 call、apply 或者 bind,这些值在调用时会被忽略,实际应用的是默认绑定规则**
function foo(p1,p2) {
this.val = p1 + p2;
}
var bar = foo.bind( null, "p1" );
var baz = {
bar: bar
};
baz.bar( "p2" );
console.log(baz.val); // undefined
console.log(window.val); //p1p2
总是使用 null 来忽略 this 绑定可能产生一些副作用。如果某个函数确实使用了 this(比如第三方库中的一个函数),那默认绑定规则会把 this 绑定到全局对象(在浏览 器中这个对象是 window),这将导致不可预计的后果(比如修改全局对象)。
可以使用Object.create(null),它和 {} 很像,但是并不会创建 Object.prototype 这个委托,所以它比 {}“更空。
间接引用
function foo() {
console.log(this.a);
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
(p.foo = o.foo)(); // 2
软绑定:
Function.prototype.softBind = function(obj) {
var fn = this;
// 捕获所有 curried 参数
var curried = [].slice.call(arguments, 1);
var bound = function() {
return fn.apply(
(!this || this === (window || global)) ? obj : this,
curried.concat.apply(curried, arguments)
);
};
bound.prototype = fn.prototype;
return bound;
};
Function.prototype.apply(thisArg[, argsArray])
在指定 this 值和参数的情况下调用某个函数。
argsArray可以是类数组(arguments...)。
function foo() {
console.log(arguments);
}
foo.apply({}, {0:1, 1:2, length: 2}); // [1,2]
Function.prototype.call(thisArg[, arg1[, arg2[, ...]]])
在指定的this值和若干个指定的参数值的前提下调用某个函数或方法。
Function.prototype.bind(thisArg[, arg1[, arg2[, ...]]])
bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。
Function.prototype.toString()
返回一个表示当前函数源代码的字符串
一个函数在自身内部调用自身: