Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JS中小括号的用法总结(自执行函数) #12

Open
imyangyong opened this issue Jun 26, 2019 · 0 comments
Open

JS中小括号的用法总结(自执行函数) #12

imyangyong opened this issue Jun 26, 2019 · 0 comments
Labels
JavaScript New feature or request

Comments

@imyangyong
Copy link
Collaborator

imyangyong commented Jun 26, 2019

作者:杨勇

小括号用法

1. 分组运算符

// 以改变表达式的优先级
var a = 4 * (1 + 2);
console.log(a);// 12

2.函数声明和函数调用参数列表:

function func(a,b){
  //代码
}
func(1,2);

// 参数列表注意点
function a(b=222,c=333) {
	console.log(b);// 111
	console.log(arguments[0]);// 111
}
a(111);

3.在循环语句中调用

if(a>1){
  //代码
}
while(i<len){
  //代码
}
for(var i=0;i<len;i++){
  //代码
}

4.函数声明,函数表达式,立即调用函数

函数声明必须带有标识符,也就是函数名,但是函数表达式方式,标识符带不带都可以。

// 函数声明(会提前)
function func(){
  // ...
}
// 函数表达式(不会提前)
var func = function(){
  // ...
}

要区分一个代码是函数声明还是函数表达式,那要看代码的应用上下文。

  • 如果有运算符号,那它就是函数表达式。(运算符包括赋值号括号等等)
  • 如果没有运算符号而且不在条件判断语句中,就说明是函数声明,无法直接加()进行调用
  • 如果在条件语句中,函数声明也会强行被当做函数表达式执行。

所以如果函数被当做函数表达式来进行调用时,this指向始终为window 间接引用

var a = {
	b:function() {
		console.log(this)
	}
};

var x = (c=a.b);
x();// window

三种自执行函数

(function(){
    //代码1
})();

(function(){
  //代码2
}());

!function(){
    //代码3
}();

上面三个语句都是在运算符相关上下文中,所以都是表达式,就可以强制调用了,下面做一下分析:

  1. (function(){})()

(function(){})是一个表达式,会强制其理解成函数直接量1方式,也就是表达式方式创建函数,(function(){})它会返回函数对象的引用,最后使用小括号()调用此函数。

  1. (function(){}())
    • 如果不用外面包裹的小括号,{}就会理解为复合语句,那么function(){}就被理解为函数声明,但是没有标识符即函数名,所以会报错。
    • 使用小括号以后,就会变成表达式,也会被理解为直接量方式。
  1. !function(){}()

原理同上,!也是一个运算符,所以在原理同上。还可以有其他运算符。

所以只要通过运算符将语句改成了该表达式,不管这是个什么样的表达式< (,),-,+, >,都会先返回函数体的引用,然后再执行其他操作,如进行调用()。

var x = "";
//只要有运算符号,就不会报错
x + function() {console.log(this)}();
x - function() {console.log(this)}();
x * function() {console.log(this)}();

  var x = 1;
  x + function() {return 2;}();
  console.log(x);// 1
// 函数体不能直接调用,调用的是函数所在的地址
function (){console.log(123)}();// 报错
function asdfas(){console.log(123)}();// 报错

// 但是如果存在运算符号<(,),-,+,*>则是先忽略执行函数的 () 
var x = function (){console.log(123)}();
var y = function asdfas(){console.log(123)}();

// 效果类似于先将函数体的地址拿到类似于
var x = function (){console.log(123)};// 拿到了函数体的地址
var y = function asdfas(){console.log(123)};// 拿到了函数体的地址

// 通过地址进行调用
x();
y();

练习题:

function(){}
(function(){})
function f(x){ return x + 1 }()
function f(x){ return x + 1 }(1)

解析:

  • 第一行代码:因为JavaScript将function关键字当作一个函数声明语句的开始,而函数声明语句function关键字后面应该是函数名,这里后面跟圆括号,当然会报错。

  • 第二行代码:给它加上一对圆括号,解析器会把()里的当做表达式去解析,在这里就会当做匿名函数表达式解析,所以不会报错。

  • 第三行代码:在一条语句后面加上()会被当做分组操作符,分组操作符里必须要有表达式,所以这里报错;

  • 第四行代码:在一条函数声明语句后面加上(1),仅仅是相当于在声明语句之后又跟了一条毫无关系的表达式,等价于下面代码:

    function f(x){ return x + 1 }
    (1)     //1

    所以返回了无关紧要的答案;

答案:

  1. 报错
  2. 不报错,无反应
  3. 报错
  4. 不报错,返回1

附录1:函数直接量

函数直接量定义方式定义出来的函数是一个匿名函数,尽管通过表达式赋值语句将这个函数赋值给了左侧的变量名。

但是,左侧的变量被赋值的是这个匿名函数的引用。

虽然函数直接量创建的是匿名函数,但是它的语法也规定它可以指定函数名。只不过这个函数名只在编写调用自身的递归函数时非常有效。为了说明这个问题,先看下面的代码:

var sum = function temp(n){
  return n == 1 ? 1 : n * temp(n-1);
}
alert(sum(5));//120
alert(temp(5));//出错

上面的代码sum是一个计算n的阶乘(n!)运算,我们可以看到,sum的定义是采用函数直接量方式定义的。但是这里存在一个问题,就是它存在一个函数名:temp。确切的讲,temp不是函数名,因为为前面已经说了,表达式右侧的function是一个匿名函数,因此,temp不可能是函数名(要不怎么叫匿名函数呢?)。但是,为什么这样的定义语法没有错误呢?因为Javascript允许指定它的函数名,但是这个函数名不是真正的函数名,它是用来为它自身递归调用时使用的。(这个函数体中实际上就是一个递归函数)。

我大致把temp理解为匿名函数内部作用域中的函数名,它可以这样调用它自己。

因此,在执行到alert(temp(5))时,浏览器报错了,告诉我们temp是未定义的。

❌Uncaught ReferenceError: temp is not defined

附录2:间接引用

var a = {
	b:function() {
		console.log(this);
	}
};
a.b();//第一行 a对象
(a.b)();//第二行 a对象
(a.b = a.b)();//第三行 window对象

解析1(高级编程书P.183)

  • 第一行是最普通的调用方式即a.b()所以得到的结果是a对象,因为由a对象调用。
  • 第二行通过把函数体包起来(a.b)()再执行,结果也是a对象,因为a.b(a.b)的定义是相同的。
  • 第三行代码是先执行一个赋值语句,然后再调用赋值后的结果。因为这个赋值表达式的值是函数本身,所以this的值不能得到维持,返回了window。

间接引用指的是不直接通过对象本身来,调用它自己内部的方法,而是类似于通过将方法的地址传递给一个变量来进行调用的如var x = function(){ console.log(1)}没有其他无关的对象,所以类似直接全局调用函数体。

解析2(你不知道的JS上卷P.97)

  • 我们可能有意无意的创建一个函数的间接引用,调用这个函数会应用默认的绑定规则。
    a.b = a.b的返回值是目标函数的引用,因此调用的的位置是匿名函数
function() {
		console.log(this);
	}

而不是等号前面的a.b,或者等号后面的a.b,根据我们之前所说的,这里会应用默认绑定。

注意:

对于默认绑定来说,决定this绑定对象的并不是调用位置是否处于严格模式,而是函数体是否处于严格模式。如果函数体处于严格模式,this会被绑定到undefined,否则this会被绑定到全局对象。

例子

var a = {
	b:{
		c:function(){
			console.log(this);
		}
	}
};
(a = a.b).c();// 前面括号返回的是b对象,与等号前面的值都是无关的。

但是现在的b处于顶端最高的权力点,返回值相当于是window.b,所以整体打出的就是b这个对象。

参考

  1. JS中小括号的用法总结(自执行函数)
  2. Javascript的函数直接量定义
  3. 间接引用
  4. Javascript中的表达式和语句

Footnotes

  1. 函数直接量:它是一个表达式而不是语句。

@imyangyong imyangyong added the JavaScript New feature or request label Jun 28, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
JavaScript New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant