WEB基础之:JavaScript函数
JavaScript函数
1. 浏览器内置函数
console.log(value);
myArray.join();
myString.replace();
var myNumber = Math.random()
2. 自定义函数
function myFunction() {
alert('hello');
}
3. 调用函数
myFunction()
// calls the function once
3.1 函数作用域
-
函数的内声明在函数未被调用之前,已经创建;
-
若存在作用域链(函数套用函数),也是在函数未被调用之前,已经创建;
-
函数内部变量优先提前声明
-
形式参数
-
局部变量
-
函数表达式
function foo(arg) { console.log(arg); var arg = 1; console.log(arg); function arg(){}; console.log(arg); } foo(2) /* function arg() 1 1 */ 假设当前活动对象为AO; // 初始 AO.arg = undefind; //
-
-
在执行函数之前,解析器会先查找并声明函数内部的声明;
4. 匿名函数
function() {
alert('hello');
}
//没有函数名称的叫做匿名函数,它也不会自己做任何事情。 通常将匿名函数与事件处理程序一起使用
var myButton = document.querySelector('button');
myButton.onclick = function() {
alert('hello');
}
//还可以将匿名函数分配为变量的值
var myGreeting = function() {
alert('hello');
}
myGreeting();
//有效地给变量一个名字;还可以将该函数分配为多个变量的值,
var anotherGreeting = function() {
alert('hello');
}
anotherGreeting();
主要使用匿名函数来运行负载的代码以响应事件触发(如点击按钮),使用事件处理程序。如:
<button id="btn">mybutton</button>
let myButton = document.getElementById('btn')
myButton.onclick = function() {
alert('hello');
}
5. 返回值
指函数执行完毕后返回的值。
<button id="btn">mybutton</button>
let myButton = document.getElementById('btn')
myButton.onclick = function() {
return 'hello!'
}
6.立即执行函数Immediately-Invoked Function Expression (IIFE)
自执行匿名函数的代码: (function () { selfFunction() } )();
立即执行匿名函数的代码: (function () { /*code*/ } )();
6.1 函数的声明与执行过程
// 由于该function里返回了另外一个function,其中这个function可以访问自由变量i
// 所有说,这个内部的function实际上是有权限可以调用内部的对象。
function makeCounter() {
// 只能在makeCounter内部访问i
var i = 0;
return function () {
console.log(++i);
};
}
// 调用函数方式一:直接调用。
makeCounter();
/*返回的是函数的表达式。后面的文章中再做深究。
ƒ () {
console.log(++i);
}
*/
// 调用函数方式二:变量赋值调用。
// counter和counter2是不同的实例,分别有自己范围内的i。
var counter = makeCounter();
counter(); // 返回:1
counter(); // 返回:2
var counter2 = makeCounter();
counter2(); // 返回:1
counter2(); // 返回:2
alert(i); // ReferenceError: i is not defined(因为i是存在于makeCounter内部)。
6.2 问题的核心
“函数名”+“()” = “执行函数”;由此表达式我们想:当声明类似function foo(){}或var foo = function(){}函数的时候。在后面加个“()”是否可以实现自执行?
// 如下实例:因为foo仅仅是function() { /* code */ }这个表达式的一个引用
var foo = function(){ console.log("foo(){} ()") }
// 直接对function() { /* code */ }后面加上“()”是否可以自动执行?
function(){ console.log("foo(){}();") }();
// 报错:Uncaught SyntaxError: Function statements require a function name
// 分析: function 是声明函数关键字,若非变量赋值方式声明函数,默认其后面需要跟上函数名的。
// 为上面的错误代码加上一个名字。
function foo(){ console.log("foo(){}();") }();
// 报错:Uncaught SyntaxError: Unexpected token ')'
// 分析: 分组操作符需要包含表达式(如此处的“()”)。
// 在传入一个表达式,将不会有异常抛出
function foo(){ console.log("foo(){}();") }(1);
// 返回:1
// 但是foo函数依然不会执行,因为它完全等价于下面这个代码
function foo(){ console.log("foo(){}();") }
(1);
6.3 立即执行函数表达式
要解决上述问题,我们只需要用“()”将代码的代码全部括住就行了,因为JavaScript里“()”里面不能包含语句,所以在这一点上,解析器在解析function关键字的时候,会将相应的代码解析成function表达式,而不是function声明。
// 下面2个括弧()都会立即执行
(function () { "(foo(){}());"} ()); // 推荐使用这个
(function () { "(foo(){})();" })(); // 但是这个也是可以用的
// 由于括弧()和JS的&&,异或,逗号等操作符是在函数表达式和函数声明上消除歧义的
// 所以一旦解析器知道其中一个已经是表达式了,其它的也都默认为表达式了
var i = function () { return 10; } ();
true && function () { /* code */ } ();
0, function () { /* code */ } ();
// 如果不在意返回值,也不在意代码的可读性
// 你甚至可以在function前面加一元操作符号
!function () { /* code */ } ();
~function () { /* code */ } ();
-function () { /* code */ } ();
+function () { /* code */ } ();
// 也可以使用new关键字
// http://twitter.com/kuvos/status/18209252090847232
new function () { /* code */ }
new function () { /* code */ } () // 如果需要传递参数,只需要加上括弧()
// =号也会将相应的代码解析成function表达式,而不是function声明。
//此处要注意区分 i 和 j 不同之处。前者是函数自执行后返回值给 i ;后者是声明一个函数,函数名为 j 。
var i = function () { console.log("Output i:"); return 1; } (); // Output i:
var j = function () { console.log("Output j:"); return 2;}
var k = (function () { console.log("Output k:"); return 3;} ()) // Output k:
console.log(i); // 10
console.log(j); // ƒ () { console.log("output j:"); return 99;}
console.log(k); // 3
6.4 用闭包保存状态
按顺序输出for循环的语句。
// 这种写法是没有用的,因为"i"并没没有被锁住,因此每次点击时,for循环已经执行完毕,"i"才会获取到值,无论点击哪个链接,最终显示的都是"I am link #' + 10"(如果有10个a元素的话)
var elems = document.getElementsByTagName( 'a' );
for ( var i = 0; i < elems.length; i++ ) {
elems[ i ].addEventListener( 'click', function(e){
e.preventDefault();
alert( 'I am link #' + i );
}, 'false' );
}
// 这个是有用的,因为在自执行函数中,"i"值会作为lockedInIndex存在;当执行完循环后,即使"i"值最终依然等于a元素个数的总和,但当循环执行时,每次遍历时自执行函数也会被调用;所以当点击连接的时候,结果正确显示。
var elems = document.getElementsByTagName( 'a' );
for ( var i = 0; i < elems.length; i++ ) {
(function( lockedInIndex ){
elems[ i ].addEventListener( 'click', function(e){
e.preventDefault();
alert( 'I am link #' + lockedInIndex );
}, 'false' );
})( i );
}
// 你也使用下面这种方式,但是相对来说,上面的代码更具可读性
var elems = document.getElementsByTagName( 'a' );
for ( var i = 0; i < elems.length; i++ ) {
elems[ i ].addEventListener( 'click', (function( lockedInIndex ){
return function(e){
e.preventDefault();
alert( 'I am link #' + lockedInIndex );
};
})( i ), 'false' );
}
6.5 自执行匿名函数和立即执行的函数表达式区别.
// 自执行的函数:函数内部调用自身,递归
function foo() { foo(); }
// 自执行的匿名函数:因为没有标示名称
// 必须使用arguments.callee属性来执行自己
var foo = function () { arguments.callee(); };
// 这“可能”也是一个自执行的匿名函数,仅当foo标示名称指向该函数。
// 如果你将foo改变成其它的,你将得到一个used-to-self-execute匿名函数
var foo = function () { foo(); };
// 立即执行函数:这个不是自执行的匿名函数,因为它没有调用自身;
(function () { /* code */ } ());
// 立即执行的函数:为函数表达式添加一个标示名称(命名函数表达式),可以方便Debug
// 但一经命名,这个函数就不再是匿名的了
(function foo() { /* code */ } ());
// 立即执行的函数表达式(IIFE)也可以自执行,不过可能不常用罢了
(function () { arguments.callee(); } ());
(function foo() { foo(); } ());
// 创建函数并立即执行;
(function(arg) {
console.log(arg);
})(1)
// arg 为立即执行函数的参数,()(1)后一个括号1为传入arg值为1
注: arguments.callee
在 deprecated in ECMAScript 5 strict mode 中被废弃了。
总结:立即执行的函数没有调用自身,自执行函数调用自身;
### 参考内容
7. eval()
eval()
函数会将传入的字符串当做 JavaScript 代码进行执行。
eval()
是全局对象的一个函数属性。eval()
的参数是一个字符串。如果字符串表示的是表达式,eval()
会对表达式进行求值。如果参数表示一个或多个 JavaScript 语句,那么eval()
就会执行这些语句。不需要用 eval()
来执行一个算术表达式:因为 JavaScript 可以自动为算术表达式求值。
如果 eval()
的参数不是字符串, eval()
会将参数原封不动地返回。
eval(2 + 2)
// 4
eval(new String('2 + 2'))
// String { "2 + 2" }
// 绕过非字符串限制
var expression = new String("2 + 2");
eval(expression.toString());