4.(ECMAScript)es6完全解读(3)

1. 重点提炼

  • 函数参数
  • 扩展运算符
  • rest参数
  • 箭头函数

2. 函数的参数

  • 参数的默认值
  • 与结构赋值结合
  • length属性
  • 作用域
  • 函数的name属性

2.1 参数的默认值

function foo(x, y){
    console.log(x, y)
}
foo('hello')

image-20201120235711518

但是针对不传的值就是undefined,这种设计也非常不好。

es5处理 => 逻辑或

function foo(x, y){
    y = y || 'world'
    console.log(x, y)
}
foo('hello')

image-20201120235915093

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.38
Branch: branch02

commit description:a1.38(函数的参数——es5处理针对不传的值就是undefined)

tag:a1.38


function foo(x, y){
    y = y || 'world'
    console.log(x, y)
}
foo('hello', 0)

如果第二个参数传递0就会发生问题,0也是false

因此es5的这种写法并不严谨。

image-20201120235915093

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.39
Branch: branch02

commit description:a1.39(函数的参数——es5处理针对不传的值不严谨)

tag:a1.39


es6可以设置函数默认值

function foo(x, y = 'world'){
    console.log(x, y)
}
foo('hello', 0)

note:函数参数是从左到右解析,如果没有默认值会被解析成 undefined

image-20201121102249292

foo('hello')

image-20201121102349315

综上es6的函数默认值写法,写法更为简洁,并且对于阅读代码更为便捷,更好优化。

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.40
Branch: branch02

commit description:a1.40(函数的参数——es6参数默认值)

tag:a1.40


es6函数参数的细节 =>

function foo(x = 5) {
    let x = 1
}
foo()

设置了参数,就不可以在函数内部再声明一个同名变量了。

image-20201121105228610

function foo(x = 5) {
    const x = 1
}
foo()

const常量同名也不允许。

image-20201121105953324

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.41
Branch: branch02

commit description:a1.41(函数的参数——设置了参数,就不可以在函数内部再声明一个同名变量/常量了)

tag:a1.41


function foo(x, x, y = 5) {
}
foo(1, 2)

函数参数名不可以重名

image-20201121115442230

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.42
Branch: branch02

commit description:a1.42(函数的参数——函数参数名不可以重名)

tag:a1.42


function foo(x, y = 5, z) {
    console.log(x, y, z)
}
foo(1, 2)

传多个参数,默认值放在中间,调用时无法跳过中间的参数去传后面的参数。因此参数的默认值一定要放在函数参数的末尾。

image-20201121115823880

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.43
Branch: branch02

commit description:a1.43(函数的参数——传多个参数,默认值放在中间,调用时无法跳过中间的参数去传后面的参数。)

tag:a1.43


正确的传参方式 =>

function foo(x, z, y = 5) {
    console.log(x, y, z)
}
foo(1, 2)

image-20201121120411486

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.44
Branch: branch02

commit description:a1.44(函数的参数——参数的默认值一定要放在函数参数的末尾)

tag:a1.44


如果想让具体某个参数使用默认值,可以使用 undefined进行赋值,如下段代码所示:.

function f(x, y = 7, z = 42) {
    return x + y + z
}
console.log(f(1, undefined, 43)) // 51

ES6中不仅可以给参数默认赋值具体的数值,同时参数赋值支持参数的逻辑运算进行赋值,如下段代码所示:

function f(x, y = 7, z = x + y) {
    return z * 0.5
}

console.log(f(1, 7)) // 4

应用 =>

function ajax(url, {
    body = '',
    method = 'GET',
    headers = {}
} = {}) {
    console.log(method)
}

ajax('http://www.abc.com', {
    method: 'POST'
})

注意

在函数体内,有时候需要判断函数有几个参数,一共有2个办法。

ES5中可以在函数体内使用 arguments来判断。

function foo(a, b = 1, c) {
    console.log(arguments.length)
}
foo('a', 'b') //2

然而在 ES6中不再使用 arguments来判断了,但可以借助 Function.length来判断。

function foo(a, b = 1, c) {
    console.log(foo.length)
}
foo('a', 'b') // 1

注意: Function.length 结果和 arguments 的结果不同!没错,Function.length 是统计第一个默认参数前面的变量数

function foo(a = 2, b = 1, c) {
    console.log(foo.length)
}
foo('a', 'b') // 0

2.2 与解构赋值结合

function foo({x, y = 5}){
    console.log(x, y)
}

foo({})
foo({
    x: 1,
    y: 2
})

解构空对象调用,则为undefined。

否则根据参数名一一对应即可。

image-20201121120750894

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.45
Branch: branch02

commit description:a1.45(函数的参数——与结构赋值结合使用)

tag:a1.45


function foo({x, y = 5}){
    console.log(x, y)
}

foo()

有参数不传必然报错,因此加上解构更为稳妥。

image-20201121120941718

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.46
Branch: branch02

commit description:a1.46(函数的参数——有参数不传必然报错)

tag:a1.46


对于发送ajax请求,URL地址必传,剩下可以选传,定义为一个对象参数,里面的header也是一个对象,第二个参数定义一个对象如果不传默认值为空对象。

function ajax(url, {
    body = '',
    method = 'GET',
    headers = {}
} = {}){
    console.log(method)
}

ajax('http://www.abc.com', {
   method: 'POST'
})

image-20201121121559963

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.47
Branch: branch02

commit description:a1.47(函数的参数——与解构配合的复杂传参)

tag:a1.47


2.3 length属性

length属性 => 返回参数个数

function foo(x, y, z){
    console.log(x, y)
}
console.log(foo.length)

image-20201121122017981

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.48
Branch: branch02

commit description:a1.48(函数的参数——length属性 => 返回参数个数)

tag:a1.48


function foo(x, y, z = 3){
    console.log(x, y)
}
console.log(foo.length)

function foo1(x = 1, y = 2, z = 3){
    console.log(x, y)
}
console.log(foo1.length)

注意:length返回的并不是参数个数,而是非默认值参数个数。

image-20201121122155589

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.49
Branch: branch02

commit description:a1.49(函数的参数——length属性 => 注意:length返回的并不是参数个数,而是非默认值参数个数。)

tag:a1.49


2.4 作用域

在函数中一旦设置了参数默认值,就会形成参数单独的作用域。如果不设置默认参数,这种语法是不会出现的。

let x = 1
function foo(x, y = x){
    console.log(y) // 2
}
foo(2)

在函数当作参数y的默认值是x,当调用函数时,参数会形成一个单独的作用域,这里面x指向当前作用域的x,而不是全局的x

image-20201121122627883

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.50
Branch: branch02

commit description:a1.50(函数的参数——在函数中一旦设置了参数默认值,就会形成参数单独的作用域。)

tag:a1.50


let x = 1
function foo(y = x) {
    let x = 2
    console.log(y) // 1
}
foo()

foo在调用的时候,y = x 参数会形成一个单独的作用域,在这个作用域里x并没有定义,这时会沿着作用域链往外去寻找x,最终在全局作用域下就找到了对应的x

image-20201121123547844

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.51
Branch: branch02

commit description:a1.51(函数的参数——在函数作用域链。)

tag:a1.51


function foo(y = x){
    let x = 2
    console.log(y)
}
foo()

在整个作用域链上外部都找不到x,因此会报错。

image-20201121124441885

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.52
Branch: branch02

commit description:a1.52(函数的参数——在函数作用域链上找不到参数对应变量。)

tag:a1.52


2.5 函数的name属性

console.log((new Function).name) //anonymous

通过new Function定义一个方法,这个方法没有名字,直接获取name属性。

匿名函数的name属性 => anonymous匿名的

image-20201121124923903

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.53
Branch: branch02

commit description:a1.53(函数的参数——anonymous。)

tag:a1.53


function foo(){}
console.log(foo.name)

image-20201121141608985

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.54
Branch: branch02

commit description:a1.54(函数的参数——函数的name属性默认为函数名)

tag:a1.54


function foo(x, y){
    console.log(this, x, y)
}

foo.bind({name: 'lisi'})(1, 2)
console.log(foo.bind({}).name)

通过bind后的函数name属性 => 多添加了一个单词bound,后面才是函数名称。

image-20201121141808618

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.55
Branch: branch02

commit description:a1.55(函数的参数——通过bind后的函数name属性 => 多添加了一个单词bound,后面才是函数名称)

tag:a1.55


匿名函数通过bind后的name属性中只有bound

console.log((function(){}).bind({}).name)

bound

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.56
Branch: branch02

commit description:a1.56(函数的参数——匿名函数通过bind后的name属性中只有bound。)

tag:a1.56


3. 扩展运算符与rest参数

  • => 扩展运算符与rest参数
  • 扩展运算符:把数组或者类数组展开成用逗号隔开的值 (特征:放在等号右边或者放在实参上)
  • rest参数:把逗号隔开的值组合成一个数组 (特征:放在等号左边或者放在形参上)
  • Spread Operator 和 Rest Parameter 是形似但相反意义的操作符,简单的来说 Rest Parameter 是把不定的参数“收敛”到数组,而 Spread Operator 是把固定的数组内容“打散”到对应的参数。

3.1 扩展运算符

// 扩展运算符
function foo(a, b, c) {
    console.log(a, b, c)
}
let arr = [1, 2, 3]
foo(...arr)
console.log(...arr)

=> 把数组或者类数组展开成用逗号隔开的值。

image-20201121144154071

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.57
Branch: branch02

commit description:a1.57(扩展运算符与rest参数—— => 把数组或者类数组展开成用逗号隔开的值。)

tag:a1.57


合并两个数组

es5语法 => 最简答的方法就是循环第一个数组,再从第二个数组中一个个push进来即可。但是比较麻烦。

let arr1 = [1, 2, 3]
let arr2 = [4, 5, 6]
Array.prototype.push.apply(arr1, arr2)
console.log(arr1)

image-20201121144555741

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.58
Branch: branch02

commit description:a1.58(扩展运算符与rest参数——es5语法 => 合并两个数组)

tag:a1.58


es6=> 扩展运算符,合并两个数组。

let arr1 = [1, 2, 3]
let arr2 = [4, 5, 6]
arr1.push(...arr2)
console.log(arr1)

image-20201121144555741

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.59
Branch: branch02

commit description:a1.59(扩展运算符与rest参数——es6 => 扩展运算符,合并两个数组。)

tag:a1.59


扩展运算符 => 将字符串打散放入数组。

let str = 'asdbbc'
var arr = [...str]
console.log(arr)

image-20201121145938211

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.60
Branch: branch02

commit description:a1.60(扩展运算符与rest参数——扩展运算符 => 将字符串打散放入数组。)

tag:a1.60


实际上扩展运算符就是把字符串、数组、类数组打散。

=> rest参数 => 将散着的元素合并。


3.2 rest参数

定义一个函数 => 求出所有参数的和,但是传入的参数个数可能不确定。

es5 =>

// rest参数
function foo(x, y, z) {
    let sum = 0
    Array.prototype.forEach.call(arguments, function(item){
        sum += item
    })
    return sum
}
console.log(foo(1, 2))
console.log(foo(1, 2, 3))

image-20201121151040699

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.61
Branch: branch02

commit description:a1.61(扩展运算符与rest参数——es5 =>定义一个函数 => 求出所有参数的和)

tag:a1.61


function foo(x, y, z) {
    let sum = 0
    Array.from(arguments).forEach(function(item){
        sum += item
    })
    return sum
}
console.log(foo(1, 2))
console.log(foo(1, 2, 3))

image-20201121151040699

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.62
Branch: branch02

commit description:a1.62(扩展运算符与rest参数——es6 =>定义一个函数 => 求出所有参数的和)

tag:a1.62


rest参数 => 接收参数

function foo(...args){
    console.log(args)
    let sum = 0
    args.forEach(function(item){
        sum += item
    })
    return sum
}
console.log(foo(1, 2))
console.log(foo(1, 2, 3))

rest参数 刚好与扩展运算符相反,它把各个参数合并成了一个数组,而扩展运算符是将一个集合打散。

image-20201121193016201

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.63
Branch: branch02

commit description:a1.63(扩展运算符与rest参数——es6 =>rest参数)

tag:a1.63


如果函数的第一个参数确定是可以传的,而后面不确定传几个参数。 => rest参数

function foo(x, ...args) {
    console.log(x)
    console.log(args)
}
foo(1, 2, 3)
foo(1, 2, 3, 4)

image-20201121194308617

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.64
Branch: branch02

commit description:a1.64(扩展运算符与rest参数——如果函数的第一个参数确定是可以传的,而后面不确定传几个参数。 => rest参数)

tag:a1.64


rest参数与解构联合使用

let [x, ...y] = [1, 2, 3]
console.log(x)
console.log(y)

image-20201121214710202

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.65
Branch: branch02

commit description:a1.65(扩展运算符与rest参数——rest参数与解构联合使用)

tag:a1.65


4. 箭头函数

  • this指向定义时所在的对象,而不是调用时所在的对象
    • es5的this指向定义时所在的对象
    • 实际在箭头函数中并没有this,箭头函数的this是它外面一层执行上下文的this。
  • 不可以当作构造函数
  • 不可以使用arguments对象
  • 可以使用rest运算符

es5中定义一个求和的函数 =>

console.log(sum(4, 5))

function sum(x, y) {
    return x + y
}

以上这种方式无论是先声明后调用,还是先调用后声明都可。实际用这种方式声明函数,会存在一个函数的预定义。

image-20201121221324431

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.66
Branch: branch02

commit description:a1.66(箭头函数——es5中定义一个求和的函数)

tag:a1.66


es5中还可以把函数赋值给一个变量。

console.log(sum)
console.log(sum(4, 5))
let sum = function(x, y){
    return x + y
}

但是这种方式,就必须先声明后调用。因为这里是将一个函数赋值给一个变量,哪怕是var声明的变量,存在变量提升,变量值是undefined的,也不可以调用,如果let,根本都不存在变量提升,因此必然会报错了。

image-20201121223412961

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.67
Branch: branch02

commit description:a1.67(箭头函数——es5中还可以把函数赋值给一个变量。)

tag:a1.67


箭头函数的左边是参数,右边是函数体。

let sum = (x, y) => {
    return x + y
}
console.log(sum(3, 4))

image-20201121223807307

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.68
Branch: branch02

commit description:a1.68(箭头函数——基本使用)

tag:a1.68


箭头函数的函数体中只有一行代码,则可以简写代码,省略花括号和return

let sum = (x, y) => x + y
console.log(sum(3, 4))

image-20201121223807307

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.69
Branch: branch02

commit description:a1.69(箭头函数——简写)

tag:a1.69


4.1 笔试题1

let x = x => x

表示的意思 =>

let x = function(x){
    return x
}

note: 如果只有一个参数,可以省略括号,如果大于一个参数一定要记得带括号

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.70
Branch: branch02

commit description:a1.70(箭头函数——笔试题1)

tag:a1.70


4.2 箭头函数与普通函数的差别

this指向定义时所在的对象,而不是调用时所在的对象

但是普通函数和箭头函数对 this的处理方式是截然不同的。

let foo = {
    name: 'es',
    say: function() {
        console.log(this.name)
    }
}

console.log(foo.say()) // es

say在被调用之后,this指向的是调用 say方法的对象,显示是 foo对象,所以 this === foo this.name 也就是 foo.name

let foo = {
    name: 'es',
    say: () => {
        console.log(this.name, this)
    }
}
console.log(foo.say()) // undefined

因为箭头函数中对 this的处理是定义时,this的指向也就是 foo外层的所指向的 window,而 window没有 name属性,所以结果是 undefined


es-demo\src\index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ES-CLI</title>
</head>
<body>
    <h1>Hello ECMAScript</h1>
    <button id="btn">点我</button>
</body>
</html>

es-demo\src\1-8.js

let oBtn = document.querySelector('#btn')
console.log(oBtn)

image-20201121233616464

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.71
Branch: branch02

commit description:a1.71(箭头函数——dom操作获取元素)

tag:a1.71


添加按钮的点击事件

let oBtn = document.querySelector('#btn')
oBtn.addEventListener('click', function () {
    console.log(this)
})

this 指向 当前触发的按钮。

image-20201122001432864

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.72
Branch: branch02

commit description:a1.72(箭头函数——添加按钮的点击事件,this 指向 当前触发的按钮。)

tag:a1.72


触发事件中引入定时器 =>

let oBtn = document.querySelector('#btn')
oBtn.addEventListener('click', function () {
    setTimeout(function(){
        console.log(this)
    }, 1000)
})

setTimeout相当于Window下的一个方法,当前调用的对象发生变化了,即this指向当前调用的对象window

image-20201122001850334

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.73
Branch: branch02

commit description:a1.73(箭头函数——添加按钮的点击事件,触发事件中引入定时器 。)

tag:a1.73


使用定时器后,不想this发生改变,es5方法 => bind

let oBtn = document.querySelector('#btn')
oBtn.addEventListener('click', function () {
    setTimeout(function(){
        console.log(this)
    }.bind(this), 1000)
})

image-20201122001432864

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.74
Branch: branch02

commit description:a1.74(箭头函数——使用定时器后,不想this发生改变,es5方法 => bind)

tag:a1.74


箭头函数 => this不发生变化

this指向定义时所在的对象,而不是调用时所在的对象

实际在箭头函数中并没有this,箭头函数的this是它外面一层执行上下文的this。

let oBtn = document.querySelector('#btn')
oBtn.addEventListener('click', function () {
    setTimeout(() => {
        console.log(this)
    }, 1000)
})

image-20201122001432864

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.75
Branch: branch02

commit description:a1.75(箭头函数——使用定时器后, => this不发生变化)

tag:a1.75


不可以当作构造函数

es5中,定义一个类 => 函数

// 类
function People(name, age){
    console.log(this)
    this.name = name
    this.age = age
}
let p1 = new People('zhangsan', 30)
console.log(p1)

image-20201122095135488

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.76
Branch: branch02

commit description:a1.76(箭头函数——在es5中,定义一个类 => 函数)

tag:a1.76


用箭头函数

let People = (name, age) => {
    this.name = name
    this.age = age
}
let p1 = new People('zhangsan', 30)
console.log(p1)

image-20201122095427777

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.77
Branch: branch02

commit description:a1.77(箭头函数——不可以当作构造函数)

tag:a1.77


不可以使用arguments对象

let foo = (...args) => {
    console.log(arguments)
    console.log(args)
}
foo(1, 2, 3)

image-20201122095724937

因为webpack中的eval函数影响的,在浏览器中看看:

image-20201122095857693

虽然箭头函数并不支持arguments,但是可以使用rest参数。

image-20201122095929053

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a1.78
Branch: branch02

commit description:a1.78(箭头函数——不可以使用arguments对象)

tag:a1.78




(后续待补充)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值