文章说明:本文章为拉钩大前端训练营所做笔记和心得,若有不当之处,还望各位指出与教导,谢谢 !
一、 ES2015字符串的扩展方法
- startsWith()
- .endsWith()
- .includes()
const message = 'Error:foo is not defined.'
console.log(
// message.startsWith('Error')
// message.endsWith('.')
// message.includes('foo')
)// true
二、ES2015 参数默认值
function foo(enable = true){//在形参的后面通过等号设置一个默认值,此时所设置的默认值只会在调用时没有传递实参或者实参传递进来是一个undefined时才会被使用
enable = enable === undefined ? true:enable
console.log('foo invoked - enable:')
console.log(enable)
}
foo(true)
若有多个形参,则设置默认值的形参要放到参数列表的最后面
function foo(bar,enable = true){
enable = enable === undefined ? true:enable
console.log('foo invoked - enable:')
console.log(enable)
}
三、ES2015 剩余参数
-
对于未知个数的参数,以前使用arguments对象接收,arguments对象是伪数组
function foo(){
console.log(arguments)//对于未知个数的参数,以前使用arguments对象接收,arguments对象是伪数组
}
console.log(1,2,3,4)
-
剩余参数:...args ,此时形参就会以数组的方式接收当前这个参数位置开始往后所有的实参,只能出现在形参列表的最后一位,而且只能出现一次
function foo(first,...args){//剩余参数:...args ,此时形参就会以数组的方式接收当前这个参数位置开始往后所有的实参,只能出现在形参列表的最后一位,而且只能出现一次
console.log(args)
}
console.log(1,2,3,4)
四、展开数组
const arr = ['foo','bar','baz']
console.log.apply(console,arr)//apply()可以以数组的方式接收我们的实参列表,apply方法里第一个参数是this指向,为console对象 //foo bar baz
console.log(...arr)//...arr 自动把数组里每个成员按照次数传递到列表当中
五、箭头函数
-
箭头左边是参数列表,多个参数则用括号括起来,右边是函数体,返回函数体执行完的值
function inc(number){
return number + 1
}
可转化为:
const inc = (n,m) => n+1//箭头左边是参数列表,多个参数则用括号括起来,右边是函数体,返回函数体执行完的值
-
若函数体内有多个函数语句,则用花括号包起来,返回值则需要用return返回
const inc = (n,m) => { //若函数体内有多个函数语句,则用花括号包起来,返回值则需要用return返回
console.log('inc invoked')
return n + 1
}
console.log(inc(100))
六、箭头函数与this
- 箭头函数不会改变this的指向
// 箭头函数与this
const person = {
name:'tom',
sayHi:function(){
console.log(`hi,my name is ${this.name}`)
}
}
person.sayHi()// hi,my name is tom
//======================================================================
const person = {
name:'tom',
sayHi:() => {// 箭头函数当中没有this的机制,所以不会改变this的指向,在箭头函数的外面this是什么,那里面拿到的this就是什么
console.log(`hi,my name is ${this.name}`)
}
}
person.sayHi() //hi,my name is undefined
const person = {
name:'tom',
sayHi:() => {
console.log(`hi,my name is ${this.name}`)
},
sayHiAsync:function(){
setTimeout(function (){
console.log(this.name)//函数内部无法拿到当前作用域的this,因为这个函数在setTimeout里面,最终会放在全局对象上被调用,所以无法拿到当前作用域的this对象,拿到的全局对象
},1000)
}
}
person.sayHiAsync()// hi,my name is undefined
//==============================================================
const person = {
name:'tom',
sayHi:() => {
console.log(`hi,my name is ${this.name}`)
},
sayHiAsync:function(){
const _this = this
setTimeout(function (){
console.log(_this.name)//使用闭包的机制拿到当前作用域的this
},1000)
}
}
person.sayHiAsync()// tom
const person = {
name:'tom',
sayHi:() => {
console.log(`hi,my name is ${this.name}`)
},
sayHiAsync:function(){
setTimeout(() =>{
console.log(this.name)//有了箭头函数就不用使用闭包机制这么麻烦了,因为箭头函数当中的this始终指向当前作用域里面的this
},1000)
}
}
person.sayHiAsync()//tom
七、ES2015对象字面量的增强
-
若变量名与要添加到对象当中的属性名一致的话,则可以省略调冒号和后面的变量名
const bar = '345'
const obj = {
foo:123,
//bar:bar //若变量名与要添加到对象当中的属性名一致的话,则可以省略调冒号和后面的变量名
bar
}
console.log(obj)//{foo:123,bar:'345'}
const obj = {
foo:123,
// method1:function(){ //可以去掉冒号和function
// console.log('methiod11')
// }
method1(){
console.log('method111')
console.log(this)
},
[1+1]:123//把属性名位置用一对方括号包起来,里面可以使用任意表达式,这个表达式的执行结果会作为这个对象的属性的属性名
}
console.log(obj)
obj.method1()//{foo:123,bar:'345',method1:[Function:method1]} 打印的是调用该方法的当前对象
八、ES2015 对象扩展方法(object.assign(),Object.is())
//多个源对象中的属性复制到一个目标对象中,若对象之间有相同的属性,那么源对象的属性就会覆盖掉目标对象的属性,源对象和目标对象都是普通的对象,源对象里面取,往目标对象里面放
const source1 = {
a:123,
b:123
}
const source2 = {
b:789,
d:789
}
const target = {
a:456,
c:789
}
const result = Object.assign(target,source1,source2)// a属性被覆盖掉了,用后面对象的属性覆盖第一个对象
console.log(target)//{ a: 123, c: 789, b: 789, d: 789 }
console.log(result === target) //true
function func(obj){
obj.name = 'func obj'
console.log(obj)
}
const obj = {name:'global obj'}
func(obj)//{ name: 'func obj' }
console.log(obj)//{ name: 'func obj' }//方法外部的对象数据被修改
//==================================================
//若是希望函数内部修改对象,使用objectassign方法,把对象复制到全新的空对象上面
function func(obj){
const funcObj = Object.assign({},obj)
funcObj.name = 'func obj'//此时内部的对象是全新的对象,它的修改不会影响外部的对象
console.log(funcObj)
}
const obj = {name:'global obj'}
func(obj)//{ name: 'func obj' }
console.log(obj)//{ name: 'global obj' }
Object.is()
方法判断两个值是否为同一个值。
Object.is()
方法判断两个值是否为同一个值。如果满足以下条件则两个值相等:
与==
运算不同。 ==
运算符在判断相等前对两边的变量(如果它们不是同一类型) 进行强制转换 (这种行为的结果会将 "" == false
判断为 true
), 而 Object.is
不会强制转换两边的值。
与===
运算也不相同。 ===
运算符 (也包括 ==
运算符) 将数字 -0
和 +0
视为相等 ,而将Number.NaN
与NaN
视为不相等.
详情见:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/is
用的较少 一般用===比较
九、proxy
Proxy 对象用于定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)。想象成门卫,不管进去拿东西还是放东西都必须经过这样一个代理,通过proxy轻松监视到读写过程
const person = {
name:'zce',
age:20
}
const personProxy = new Proxy(person,{
get (target,property){//接收两个参数,第一个是代理的目标对象,第二个是外部所访问的属性的属性名,这个方法的返回值会作为外部访问这个属性得到的结果
return property in target ? target[property] : 'default'
// console.log(target,property)// {name:'zce',age:20} name
// return 100
},
set (target,property,value){//接收三个参数,代理目标对象,要写入的属性名称,写入的属性值
if(property === 'age'){
if(!Number.isInteger(value)){
throw new TypeError(`${value} is not an int`)
}
}
target[property] = value
// console.log(target,property,value)
}
})
personProxy.age = 'string' //报错
console.log(personProxy.name) // 100
Proxy 与 Object.defineProperty()的对比
const person = {
name:'zce',
age:20
}
const personProxy = new Proxy(person,{
deleteProperty(target,property){
console.log('delete',property) //delete age
delete target[property]
}
})
delete personProxy.age
console.log(person)//{name:'zce'}
// Proxy 更好的支持数组对象的监视
const list = []
const listProxy = new Proxy(list,{
set(target,property,value){// 监视数据的写入
console.log('set',property,value)
target[property] = value
return true //表示设置成功
}
})
listProxy.push(100) // set 0 100 其中0是数组下标
//Proxy 以非侵入的方式监管了对象的读写,不需要对对象本身做任何的操作,可以监视到内部成员的读写
十、Reflect
-
reflect 是一个静态类,Reflect内部封装了一系列对对象的底层操作,Reflect成员方法就是Proxy处理对象的默认实现
-
Reflect 对象 统一提供一套用于操作对象的API
const obj = {
foo:'123',
bar:'456'
}
const proxy = new Proxy(obj,{
get(target,property){
console.log('watch logic-')//watch logic~
return Reflect.get(target,property)
}
})
console.log(proxy.foo)//123
Reflect提供了一个统一的方式
const obj = {
name:'zce',
age:18
}
console.log('name' in obj)
console.log(delete obj['age'])
console.log(Object.keys(obj))
//reflect提供了一个统一的方式:
console.log(Reflect.has(obj,'name'))
console.log(Reflect.deleteProperty(obj,'age'))
console.log(Reflect.ownKeys(obj))
更多详情见mdn:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect
十一、Class类
function Person (name){
this.name = name//通过this访问当前的实例对象
}
Person.prototype.say = function(){//若想在这个类型所有实例之间共享一些成员,借助propertype原型去实现
console.log(`hi,my name is ${this.name}`)
}
//========================================================
class Person {//定义了person类型
constructor(name){//当前这个类型的构造函数
this.name = name
}
say(){
console.log(`hi,my name is ${this.name}`)
}
}
const p = new Person('tom')
p.say()
更多详情请见mdn:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/class
静态方法 static
- 在类型中的方法一般分为实例方法和静态方法,实例方法是需要通过这个类型构造的实例对象去调用,静态方法是直接通过类型本身去调用,以前实现静态方法是直接在构造函数对象上去挂载方法来实现,在js当中函数也是对象也可以添加一些方法成员
-
ES2015中新添加静态成员的static关键词
// static 方法
class Person{
constructor(name){
this.name = name
}
say(){
console.log(`hi,my name is ${this.name}`)
}
static create (name){
return new Person(name)
}
}
const tom = Person.create('tom')//静态方法的调用
tom.say()//因为静态方法是挂载到类型上面,所以静态方法内部的this就不会指向某一个实例对象而是当前的类型
Extends 继承
- 通过继承这种特性能够抽象出来相似类型之间重复的地方
class Person{
constructor(name){
this.name = name
}
say(){
console.log(`hi,my name is ${this.name}`)
}
}
class Student extends Person {//student 类型就有person类型中所有的成员了
constructor(name,number){
super(name)//super 始终指向父类,调用它则调用了父类的构造函数
this.number = number
}
hello(){
super.say()// hi,my name is jack
console.log(`my school number is ${this.number}`)// my school number is 100
}
}
const s = new Student('jack','100')
s.hello()
十二、Set与Map
-
Set 数据结构 理解为集合,与传统数组非常类似,Set内部成员是不允许重复的,每一个值在set当中都是唯一的
const s = new Set()
s.add(1).add(2).add(3).add(4).add(2)//add 方法返回集合对象本身,所以可以链式调用,若是添加了相同的则会被忽略掉
console.log(s)// Set {1,2,3,4}
s.forEach(element => {
console.log(element)//1 2 3 4
});
for(let i of s){
console.log(i)//1 2 3 4
}
console.log(s.size) //4 size相当于数组当中的length
console.log(s.has(100))//false has 判断集合当中是否存在某个特定的值
console.log(s.delete(3)) //true 删除成功后返回true
s.clear() //清除集合当中的全部内容
console.log()// Set{ }
const arr = [1,2,1,3,4,1]
const result = new Set(arr) // 重复的值会忽略掉
const result = Array.from(new Set(arr))//Array.from 把set转化为数组
const result = [...new Set(arr)]//使用...在空的数组中展开set,set中的成员变成了空数组中的成员,从而转化为了数组
Map
- Map 数据结构 跟EMCAScript里面对象十分类似,本质上他们都是键值对集合,但是对象结构中的键只能是字符串类型,存放复杂数据结构类型时会有些问题
-
Map 用任意类型的数据作为键,而对象只能够使用字符串作为键
onst obj = {}
obj[true] = 'value'
obj[123] = 'value'
obj[{a:1}] = 'value'
console.log(Object.keys(obj)) //['123','true','[object Object]'] 这些键都被转化为了字符串,如果给我们的对象添加的键不是字符串,内部将会把这个数据toSring的结果作为一个键
console.log(obj[`[object Object]`])// value
console.log(obj[{}]) //value 跟上面的重复,无法区分,Map变产生
const m = new Map()
const tom = {name : 'tom'}
m.set(tom,90)// 这里的键可以是任意类型数据,tom是键,值为90
console.log(m) // Map {
{ name:'tom'} => 90}
console.log(m.get(tom)) //90
// m.has()
// m.delete()
// m.clear()
m.forEach((value,key) =>{
console.log(value,key)// 90 {name:'tom'}
})