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 —— 函数:函数定义 #82

Open
lizhongzhen11 opened this issue Mar 5, 2020 · 0 comments
Open

重学js —— 函数:函数定义 #82

lizhongzhen11 opened this issue Mar 5, 2020 · 0 comments
Labels
js基础 Good for newcomers 重学js 重学js系列 规范+MDN

Comments

@lizhongzhen11
Copy link
Owner

lizhongzhen11 commented Mar 5, 2020

函数:函数定义

通过执行函数和类的 [[Call]] 内部方法来对它们进行求值。

思考以下代码

// 来自MDN
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/function
var foo = function() {}
foo.name // "foo"

var foo2 = foo
foo2.name // "foo"

var bar = function baz() {}
bar.name // "baz"

console.log(foo === foo2); //true
console.log(typeof baz);// undefined 
console.log(bar === baz); // false (errors because baz == undefined)

使用 function 关键字创建的普通函数求值

函数声明 : function 绑定标识符 ( 形参 ) { 函数体 }

function fn(arg) {
  // ...
}
  1. 返回 NormalCompletion(empty)

注意:规范 B.3.3 提供了替代语义

函数声明 : function ( 形参 ) { 函数体 }

// 匿名函数
button.addEventListener('click', function(event) {
  console.log('button is clicked!')
})
  1. 返回 NormalCompletion(empty)

函数表达式 : function ( 形参 ) { 函数体 }

let bar = function(arg) {}
  1. 定义 scope运行时执行上下文LexicalEnvironment
  2. 定义 closureOrdinaryFunctionCreate(%Function.prototype%, 形参, 函数体, non-lexical-this, scope)
  3. 执行 MakeConstructor(closure)
  4. closure.[[SourceText]] 赋值为由函数表达式匹配的 源文本
  5. 返回 closure

函数表达式 : function 绑定标识符 ( 形参 ) { 函数体 }

let bar = function baz(arg) {}
  1. 定义 scope运行时执行上下文LexicalEnvironment
  2. 定义 funcEnvNewDeclarativeEnvironment(scope)
  3. 定义 envRecfuncEnv环境记录
  4. 定义 name 为绑定标识符的字符串值
  5. 执行 envRec.CreateImmutableBinding(name, false) (非严格绑定,该 name 可以改变)
  6. 定义 closureOrdinaryFunctionCreate(%Function.prototype%, 形参, 函数体, non-lexical-this, funcEnv)
  7. 执行 MakeConstructor(closure)
  8. 执行 SetFunctionName(closure, name)
  9. closure.[[SourceText]] 赋值为由函数表达式匹配的 源文本
  10. 执行 envRec.InitializeBinding()
  11. 返回 closure

将为使用 函数声明 或 函数表达式 定义的每个函数自动创建一个 "prototype 属性,以允许将该函数用作 构造函数

函数语句列表 : [empty]

  1. 返回 NormalCompletion(undefined)

回顾文头代码

// 换成let,const一样
var bar = function baz() {}
bar.name // "baz"
console.log(typeof baz);// undefined 
console.log(bar === baz); // false (errors because baz == undefined)

以上代码属于 函数表达式 : function 绑定标识符 ( 形参 ) { 函数体 }声明和变量语句 共同作用的结果。

  1. 先看本文的函数创建算法,给该函数设置 name'baz',没有问题!
  2. 问题在于 声明和变量语句的算法,这里属于 绑定标识符 初始化器,那么找到 let, const 以及 var 相对应的算法,
    1. var 对应的算法内部最终走 PutValue,然后会走给全局变量设置属性这一步,也就是说,最终给全局对象设置了 bar 属性,其值为创建的函数 baz!但是,并没有把 baz 函数单独绑定到全局对象上!!!
    2. letconst 对应的算法内部走的是 InitializeReferencedBinding,其本质也是将 bar 与 函数绑定,但是并没有单独创建一个叫 baz 的变量,即函数baz 以变量 bar 的身份“活着”!
  3. 这与直接 function baz(){} 声明函数是不同的

2020-03-16 补充

函数参数问题:

// 来自 高级前端面试
function compareMembers(person1, person2 = person1) {
  if (person1 !== person2) {
    console.log('Not the same!')
  } else {
    console.log('They are the same!')
  }
}

const person = {name: 'Lydia'}
compareMembers(person) // They are the same!

MDN —— 默认参数可用于后面的默认参数

2020-07-17 补充

最近又继续看winter《重学前端》中未看的部分章节,发现个关于 函数名 的有趣问题:

const obj = {
  fn1: function fn() {
    console.log(obj.fn1.name) // 'fn'
    console.log(fn) // 函数
  },
  fn2() {
    console.log(obj.fn2.name) // 'fn2'
    console.log(fn2) // 报错
  },
  fn3: () => {
    console.log(obj.fn3.name) // 'fn3'
    console.log(fn3) // 报错
  }
}
obj.fn1()
obj.fn2()
obj.fn3()

以上三种写法有什么区别嘛?

老实说,我第一次接触时只会第一种即 fn1: function fn(){} 这种写法,后来也是看了别人的代码才发现有第二种直接 fn2(){} 的写法。其实刚接触时还是很惊奇的,心想为什么能这样写,但是,由于我菜加上都不知道该怎么问这个问题,所以就选择性忽略了。好在这个问题倒不是非常重要。

现在看了《重学前端》后,关于这个问题的疑问又涌上心头了!

1. 为什么可以在对象中像上述 fn2(){} 这样写?
重学js —— 函数:箭头函数定义和方法定义 中的 方法定义。简而言之,规范允许。

2. 上述代码中为何直接打印 fn 不报错还返回函数?
其实很简单,当局者迷罢了。

const obj = {
  fn1: function fn() {
    console.log(obj.fn1.name) // 'fn'
    console.log(fn) // 函数
  }
}

这种写法本质是:

function fn() {
  console.log(obj.fn1.name) // 'fn'
  console.log(fn) // 函数
}
const obj = {
  fn1: fn
}
obj.fn1()

这样拆开来就很好理解了吧。

2020-07-29 补充

来自高级前端面试小程序 js基础 第23题,我做错了

function changeObjProject(o) {
  o.siteUrl = 'http://www.baidu.com';
  o = new Object();
  o.siteUrl = 'http://www.google.com';
}
let webSite = new Object();
changeObjProject(webSite);
console.log(webSite.siteUrl); // 'http://www.baidu.com'

我看了一眼想都不用想说了 google。。。

其实这里坑点在于形参 o,它其实可以看做函数的一个变量,这么理解的话就容易多了,一开始这个变量和 webSite 指向一致,后来在内部又对它重新指向另一个对象了,自然不会影响 webSite

@lizhongzhen11 lizhongzhen11 added js基础 Good for newcomers 重学js 重学js系列 规范+MDN labels Mar 5, 2020
This was referenced Mar 5, 2020
@lizhongzhen11 lizhongzhen11 changed the title 重学js —— 函数和类:函数定义 重学js —— 函数:函数定义 Mar 12, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
js基础 Good for newcomers 重学js 重学js系列 规范+MDN
Projects
None yet
Development

No branches or pull requests

1 participant