WEB基础之: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 函数作用域

  • 函数的内声明在函数未被调用之前,已经创建;

  • 若存在作用域链(函数套用函数),也是在函数未被调用之前,已经创建;

  • 函数内部变量优先提前声明

    1. 形式参数

    2. 局部变量

    3. 函数表达式

      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.calleedeprecated in ECMAScript 5 strict mode 中被废弃了。

总结:立即执行的函数没有调用自身,自执行函数调用自身;

### 参考内容

  1. Immediately-Invoked Function Expression (IIFE)

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());

posted @ 2021-01-10 08:45  f_carey  阅读(9)  评论(0编辑  收藏  举报  来源