ECMAScript 6(14)函数的扩展(3)——严格模式、name、箭头函数、尾递归
来源:互联网 发布:highcharts.js 下载 编辑:程序博客网 时间:2024/04/28 23:48
函数的扩展3
写在前面的话
感觉博客写的有点太细了,导致我学习进度过于缓慢。
因此,建议学习者以阮一峰博客为主,以本博客为补充。
我这里主要写一些示例和补充,帮助理解的更为全面
0、一句话总结
- 严格模式要用全局用(稳),不然就别用(不用容易出bug,用不好也容易出bug),所以还是babel大法好
- name属性就是函数名,bind后加前置bound
- 箭头函数写起来简单粗暴又省事(就是打符号挺麻烦,没有打英文字母简单)
- 尾递归很好很强大但是默认不开启然后GG
4、严格模式
4.1、什么时候用
在函数参数使用解构赋值,默认值和三个点运算符时,不能在函数内部声明严格模式。
总而言之,如果要使用严格模式,就在该js文件的顶端添加,不要就别用,免得报错。
如果函数外面使用严格模式会报错,说明代码写的太烂了,去重构吧。
虽然也可以将该函数包括在另外一个函数的局部作用域,并对该函数使用严格模式,但这种不优雅,所以老老实实全局吧。
5、name属性
5.1、特点
原文里提到,函数的name属性是函数名;如果匿名函数赋值给变量,那么name属性就是变量名(es6)(es5为空值);
那么问题来了,假如声明一个匿名函数,赋值给foo,然后再将foo赋值给bar会怎么样?
var foo = function(){}var bar = foo;console.log(foo.name); //fooconsole.log(bar.name); //foo
说明name属性的值,取决于匿名函被赋予的第一个变量的名字。(即使bar = foo = function(){}同理,为foo)
另外,name属性是 只读 的,即赋值后修改无效。
5.2、anonymous
原文提到:
(new Function).name; //"anonymous",意思是匿名的
但是只会Funtion这样的关键字生效,如果是以下代码,则不同
function foo() {}(new foo).name; //undefined//另外注意不能写为 var foo = function(){},会报错
推测是因为new Function的时候,是调用了以下函数作为构造函数:
function anonymous() {}
从以下过程得知
var foo = new Function;foo; //function anonymous() {}
5.3、bind
在es5里,添加了bind方法。这个方法的作用是返回一个改变了this指向目标的原函数,如以下代码:
function foo() { console.log(this);}var bar = foo.bind("this");bar(); //"this",不完全准确,但可以这么理解foo(); //Window
注意,foo函数中,this指向的目标并没有改变。
而在bind绑定后,name属性将发生变化,准确的来说,是加上前缀”bound “,如代码:
function foo() { console.log(this);}var bar = foo.bind("this");bar.name; //"bound foo"
“bound”是”bind”的过去式,显而易见,是指已经被绑定过的。
6、箭头函数
6.1、写法
(param1, param2, …, paramN) => { statements; }
(param1, param2, …, paramN) => expression;
// 等价于
(param1, param2, …, paramN) => { return statements; }
(param1, param2, …, paramN) => { return expression; }
// 如果只有一个参数,圆括号是可选的:
(singleParam) => { statements; }
// 等价于
singleParam => { statements; }
// 如果箭头函数 无参数 或者 有多参数, 必须使用 ()圆括号或者 _下划线:
() => { statements; }
或
_ => { statements; }
简单来说,箭头函数分为三部分(名字都是我自己起的=.=):
- 参数部分;
- 箭头符号(固定的=>)
- 函数体部分
6.2、参数
简单来说:
没有参数可以写()或者写_(左边是下划线),或者随便什么非关键字且不影响正常使用的变量名;
一个参数就写该参数名即可;
多个参数需要用()包裹起来使用。
总之,不能什么都不写。
6.3、函数体部分
1、被{}包裹起来的情况:
- 大于一行语句;
- 无返回值;
2.不被{}包裹起来的情况:
- 只有一行语句,且需要这行语句的值作为函数的返回值
不管是被包括还是不被包括,函数体部分可以理解为一个代码块。
6.4、箭头函数的this
箭头函数的特点是不绑定this,具体来说,箭头函数的this,和其父级作用域的this保持一致;
而非箭头函数的this,和原函数的this保持一致;
如代码:
let foo = function (callback) { callback();};function test1() { foo(function () { console.log(this); })}test1(); //Windownew test1(); //Windowfunction test2() { foo(() => { console.log(this); })}test2(); //Windownew test2(); //test2 {a: "test"}
之所以最后一个指向test2这个对象,是因为通过new来创建一个函数实例时,是调用了构造器,改变了this指向的目标,而默认情况下,函数声明时,函数内部的this指向的目标是Window。
另外,因为箭头函数本身没有this,因此bind、call、apply是无效的(但不会报错),如代码:
var bar = () => { console.log(this);}var foo = bar.bind("a");foo(); //Window,绑定失败但不报错bar.apply("apply"); //Window,绑定失败但不报错bar.call("apply"); //Window,绑定失败但不报错
作为对比:
var bar = function () { console.log(this);}var foo = bar.bind("bind");foo(); //"bind"bar.apply("apply"); //"apply"bar.call("call"); //"call"
6.5、一些属性和特点
1、箭头函数不能通过new来创建函数实例,如代码:
let foo = () => { console.log(1)}new foo(); //Uncaught TypeError: foo is not a constructor
2、箭头函数没有arguments关键词。如果使用,要么取父级作用域的arguments,如果没有则返回undefined
var foo = function (a) { console.log(arguments); (() => { console.log(arguments); })()}var bar = () => { console.log(arguments);}foo(1);//[1]//[1]bar(1); //Uncaught ReferenceError: arguments is not defined
6.5、严格模式
严格模式下,this的表现略有差别(主要体现在非箭头函数),如代码:
箭头函数是一样的,都指向Window
var bar = () => { console.log(this);}bar(); //Window
'use strict'var bar = () => { console.log(this);}bar(); //Window
普通匿名函数则不同,严格模式下this的值是undefined
'use strict'var bar = function () { console.log(this);}bar(); //undefined
var bar = function () { console.log(this);}bar(); //Window
6.6、嵌套
嵌套没啥的啦,简单暴力的说,就是把里面的箭头函数放在外面的箭头函数的函数体之中。
多层嵌套道理一样,所以没啥可说的。
如果要返回值,要么要么在函数体里return,要么就让函数体变为一个表达式并不要用大括号包裹他(可以用圆括号())
反正啊,只要你会写普通函数的嵌套,那么写箭头函数的嵌套也不在话下。
7、尾调用
7.1、什么是尾调用?
简单暴力的总结,就是在函数执行结束的时候,return 返回另外一个执行的函数。如代码:
function foo(){ return bar(); //这里bar是一个函数}
如果没有return,或者return的不是一个单纯的函数,都不叫尾调用。
也就是说,最后一行代码要严格符合上面给的示例才算。
另外,尾调用的函数中(不包括传给他的参数),里面不能包括原函数中的变量。
7.2、尾调用的原理
简单来说,其实我没理解他调用栈到底是怎么运作的,怎么压栈的,毕竟我是野路子。
但还是可以帮你理解为什么能优化
(郑重提示:以下这个只是帮忙理解,实际是调用栈,更像是一个数组)
假如函数调用如以下方式嵌套:
//这个只是帮你理解而写的不准确的例子,实际是不同的!var funA = { name: "函数A", variable: "变量X", funB: { name: "函数B", variable: "变量Y", funC: { name: "函数C", variable: "变量Z", } }}
funA,funB,funC分别表示函数A、B、C,然后variable表示其调用的变量
假如funB里不包含funA的变量,那么在进行尾调用的时候,由于funA的其他东西对funB没用了,于是在执行funB的时候就可以扔掉,整个结构变为以下这样:
var funB = { name: "函数B", variable: "变量Y", funC: { name: "函数C", variable: "变量Z", }}
如果funB里还需要使用funA的变量,那么显然funA对funB还是有意义的,于是还需要继续保留。
如果funA要保留,那么就要占用更多内存,如果调用次数比较多(典型的就是递归),那么需要占用的内存空间就可以很大,于是尾调用在这方面可以节省内存的意义就体现出来了。
7.3、尾调用的问题(比如默认不开启)
根据 饿了么这篇博客所说,V8引擎虽然实现了尾调用,但是默认是不开启的?
我实践证明(Chrome版本57.0.2987.110),的确没开启,
首先,根据调用栈最多100000次(吧?),如代码:
function sum(x, y) { if (y > 0) { return sum(x + 1, y - 1); } else { return x; }}sum(1, 100000); //Uncaught RangeError: Maximum call stack size exceeded
以下是尾调用代码:
function add(n, total) { if (n === total) return n; return add(n + 1, total);}add(0, 100000); //Uncaught RangeError: Maximum call stack size exceeded
所以,尾调用没有被开启!GG思密达┑( ̄Д  ̄)┍
- ECMAScript 6(14)函数的扩展(3)——严格模式、name、箭头函数、尾递归
- EcmaScript 6 箭头函数
- javascript(EcmaScript 5)的严格模式
- ES6(七: 函数扩展)(name,箭头,函数绑定,尾调用优化)
- 解读ECMAScript 6箭头函数
- 解读ECMAScript 6箭头函数
- ECMAScript 6(13)函数的扩展(2)——...运算符
- ECMAScript 6 入门学习(6.函数的扩展)
- ES6-函数的扩展-严格模式
- ECMAScript 6(15)对象的扩展(1)——简写、属性名拼接、name
- ECMAScript 6(12)函数的扩展(1)——函数参数的默认值之详细解析
- ECMAScript 6 学习系列课程 (ES6 箭头函数的使用)
- ECMAScript 6 学习笔记----函数的扩展
- 函数的严格模式
- ES6关于函数的扩展知识(ECMAScript 6 入门笔记)
- ECMAScript 6(8)——正则表达式的扩展
- ECMAScript 6(11)——数组的扩展
- JavaScript的this机制与箭头函数(二)——箭头函数中的this
- Android ABI 概念
- E. Ice cream coloring (图论 染色 DFS)
- es6-Map
- xbin目录及toolbox,toybox
- Shell for循环
- ECMAScript 6(14)函数的扩展(3)——严格模式、name、箭头函数、尾递归
- 关于sql数据库的约束
- git 学习
- 观察者模式
- 376. Wiggle Subsequence(第十三周)
- 作业6
- 138.子数组之和
- lintcode(477)被围绕的区域
- 【C++】Huffman树的实现