1.ES6语感训练 30 题 (温故而知新)

ECMAScript 6(以下简称ES6)是 JS 语言的下一代标准,已经在20156月正式发布了。它的目标,是使得 JS 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。

在开始之前咱们先通过一些面试题来熟悉并回顾 ES6 有哪些知识点,帮助咱们快速深入了解 ES6 。

问题1:可以解释一下 ES5ES6的区别吗?

ECMAScript 5 (ES5):ECMAScript 的第五版,于2009年标准化,该标准已在所有现代浏览器中完全支持。

ECMAScript 6 (ES6)/ ECMAScript 2015 (ES2015):ECMAscript 第 6 版,2015 年标准化。这个标准已经在大多数现代浏览器中部分实现。

以下是ES5和ES6之间的一些主要区别:

箭头函数和字符串插值

const greetings = (name) => {
  return `hello ${name}`;
}

也可以这样写:

const greetings = name => `hello ${name}`;    

constconst 表示无法修改变量的原始值。需要注意的是,const表示对值的常量引用,咱们可以改变被引用的对象的属性值,但不能改变引用本身。

const NAMES = [];
NAMES.push("Jim");
console.log(NAMES.length === 1); // true
NAMES = ["Steve", "John"]; // error    

块作用域:ES6 中 let, const 会创建块级作用域,不会像 var 声明变量一样会被提升。

默认参数:默认参数使咱们可以使用默认值初始化函数。当参数省略或 undefined 时使用默认参数值。

function multiply (a, b = 2) {
   return a * b;
}
multiply(5); // 10    

类定义与继承

ES6 引入了对类(class关键字)、构造函数(constructor关键字)和 extend 关键字(用于继承)的语言支持。

for-of 运算符

for...of 语句创建一个遍历可迭代对象的循环。

展开操作符

const obj1 = { a: 1, b: 2 }
const obj2 = { a: 2, c: 3, d: 4}
const obj3 = {...obj1, ...obj2}    

Promises: Promises 提供了一种机制来处理异步操作的结果和错误。可以使用回调来完成相同的事情,但是Promises 通过方法链接和简洁的错误处理来提高可读性。

const isGreater = (a, b) => {
  return new Promise ((resolve, reject) => {
    if(a > b) {
      resolve(true)
    } else {
      reject(false)
    }
    })
}
isGreater(1, 2)
  .then(result => {
    console.log('greater')
  })
 .catch(result => {
    console.log('smaller')
 })    

模块导出

const myModule = { x: 1, y: () => { console.log('This is ES5') }}
export default myModule;   

和导入

import myModule from './myModule';

问题 2: JavaScript 的提升是什么

提升是指 JS 解释器将所有变量和函数声明移动到当前作用域顶部的操作,提升有两种类型

  • 变量提升
  • 函数提升

只要一个var(或函数声明)出现在一个作用域内,这个声明就被认为属于整个作用域,并且可以在任何地方访问。

var a = 2
foo() // 正常运行, foo 已被提升

function foo() {
  a = 3
  console.log(a)   // 3
  var a                        
}

console.log( a )   // 2

问题 3: ES6 中的临时死区是什么

在 ES6 中,letconstvarclassfunction一样也会被提升,只是在进入作用域和被声明之间有一段时间不能访问它们,这段时间是临时死区(TDZ)

//console.log(aLet)  // would throw ReferenceError

let aLet;
console.log(aLet); // undefined
aLet = 10;
console.log(aLet); // 10

问题4:varletconst 之间区别?

let是更完美的var,不是全局变量,具有块级函数作用域,大多数情况不会发生变量提升。const定义常量值,不能够重新赋值,如果值是一个对象,可以改变对象里边的属性值。

1、let声明的变量具有块级作用域
2、let声明的变量不能通过window.变量名进行访问
3、形如for(let x..)的循环是每次迭代都为x创建新的绑定

下面是var带来的不合理场景

var arr = [];
for (var i = 0; i < 10; i++) {
  arr[i] = function () {
    console.log(i);
  }
}
arr[5]() // 10

在上述代码中,变量ivar声明的,在全局范围类都有效,所以用来计数的循环变量泄露为全局变量。所以每一次循环,新的i值都会覆盖旧值,导致最后输出都是10

而如果对循环使用let语句的情况,那么每次迭代都是为x创建新的绑定代码如下:

var arr = [];
for (let i = 0; i < 10; i++) {
  arr[i] = function () {
    console.log(i);
  }
}
arr[5]() // 5

当然,除了这种方式让数组找中的各个元素分别是不同的函数,我们还可以采用ES5中的闭包和立即函数两种方法。

采用闭包

function showNum(i) {
  return function () {
    console.log(i)
  }
}
var a = []
for (var i = 0; i < 5; i++) {
  a[i] = showNum(i)(); 
}

采用立即执行函数

var a = []
for (var i = 0; i < 5; i++) {
  a[i] = (function (i) {
    return function () {
      console.log(i)
    }
  })(i)
}
a[2](); // 2

问题 5:什么是 IIFE (立即调用的函数表达式)

IIFE是一个立即调用的函数表达式,它在创建后立即执行

(function IIFE(){
    console.log( "Hello!" );
})();
// "Hello!"

常常使用此模式来避免污染全局命名空间,因为在IIFE中使用的所有变量(与任何其他普通函数一样)在其作用域之外都是不可见的。

问题 6: ES6 类和 ES5 函数构造函数有什么区别?

// ES5 Function Constructor
function Person(name) {
  this.name = name;
}

// ES6 Class
class Person {
  constructor(name) {
    this.name = name;
  }
}  

对于简单的构造函数,它们看起来非常相似。

构造函数的主要区别在于使用继承。如果咱们创建一个继承Person类的Student子类并添加一个studentId字段,以下是两种方式的使用:

// ES5 Function Constructor
function Student(name, studentID) {
  // 调用你类的构造函数以初始化你类派生的成员。
  Person.call(this. name)
  // 初始化子类的成员。
  this.studentId = studentId
}

Student.prototype = Object.create(Person.prototype)
Student.prototype.constructor = Student

// ES6 Class
class Student extends Person {
  constructor(name, studentId) {
    super(name)
    this.studentId = studentId
  }
}

在 ES5 中使用继承要复杂得多,而且 ES6 版本更容易理解和记住。

问题 7: .call.apply 区别是啥?

.call.apply均用于调用函数,并且第一个参数将用作函数中this的值。但是,.call将逗号分隔的参数作为下一个参数,而.apply将参数数组作为下一个参数。简单记忆法:C用于call和逗号分隔,A用于apply和参数数组。

 function add(a, b) {
  return a + b;
}

console.log(add.call(null, 1, 2)); // 3
console.log(add.apply(null, [1, 2])); // 3   

问题 8:何时在 ES6 中使用箭头函数?

以下是一些经验分享:

  • 在全局作用域内和Object.prototype属性中使用 function

  • 为对象构造函数使用 class

  • 其它情况使用箭头函数。

为啥大多数情况都使用箭头函数

  • 作用域安全性:当箭头函数被一致使用时,所有东西都保证使用与根对象相同的thisObject。如果一个标准函数回调与一堆箭头函数混合在一起,那么作用域就有可能变得混乱。

  • 紧凑性:箭头函数更容易读写。

  • 清晰度:使用箭头函数可明确知道当前 this 指向。

问题 9: 什么时候不使用箭头函数? 说出三个或更多的例子

不应该使用箭头函数一些情况:

  • 当想要函数被提升时(箭头函数是匿名的)

  • 要在函数中使用this/arguments时,由于箭头函数本身不具有this/arguments,因此它们取决于外部上下文

  • 使用命名函数(箭头函数是匿名的)

  • 使用函数作为构造函数时(箭头函数没有构造函数)

  • 当想在对象字面是以将函数作为属性添加并在其中使用对象时,因为咱们无法访问 this 即对象本身。

问题 10:什么是模板字符串及作用

就是这种形式${varible},在以往的时候咱们在连接字符串和变量的时候需要使用这种方式'string' + varible + 'string'但是有了模版语言后,咱们可以使用string${varible}string这种进行连接。基本用途有如下:

基本的字符串格式化,将表达式嵌入字符串中进行拼接,用${}来界定

//es5 
var name = 'lux';
console.log('hello' + name);
//es6
const name = 'lux';
console.log(`hello ${name}`); //hello lux

ES5时我们通过反斜杠(\)来做多行字符串或者字符串一行行拼接,ES6反引号(``)直接搞定。

//ES5
var template = "hello \
world";
console.log(template); //hello world

//ES6
const template = `hello
world`;
console.log(template); //hello 空行 world

问题 11: 举一个柯里化函数的例子,并说明柯里化的好处?

柯里化是一种模式,其中一个具有多个参数的函数被分解成多个函数,当被串联调用时,这些函数将一次累加一个所需的所有参数。这种技术有助于使用函数式编写的代码更容易阅读和编写。需要注意的是,要实现一个函数,它需要从一个函数开始,然后分解成一系列函数,每个函数接受一个参数。

function curry(fn) {
  if (fn.length === 0) {
    return fn;
  }

  function _curried(depth, args) {
    return function(newArgument) {
      if (depth - 1 === 0) {
        return fn(...args, newArgument);
      }
      return _curried(depth - 1, [...args, newArgument]);
    };
  }

  return _curried(fn.length, []);
}

function add(a, b) {
  return a + b;
}

var curriedAdd = curry(add);
var addFive = curriedAdd(5);

var result = [0, 1, 2, 3, 4, 5].map(addFive); // [5, 6, 7, 8, 9, 10]

问题 12: 在 ES6 中使用展开(spread)语法有什么好处? 它与剩余(rest)语法有什么不同?

ES6 的展开语法在以函数形式进行编码时非常有用,因为咱们可以轻松地创建数组或对象的副本,而无需求助于Object.create,slice或库函数。Reduxrx.js项目中经常使用此特性。

function putDookieInAnyArray(arr) {
  return [...arr, 'dookie'];
}

const result = putDookieInAnyArray(['I', 'really', "don't", 'like']); 
// ["I", "really", "don't", "like", "dookie"]

const person = {
  name: 'Todd',
  age: 29,
};

const copyOfTodd = { ...person };  

ES6 的 rest 语法提供了一种捷径,其中包括要传递给函数的任意数量的参数。

就像展开语法的逆过程一样,它将数据放入并填充到数组中而不是展开数组,并且它在函数变量以及数组和对象解构分中也经常用到。

function addFiveToABunchOfNumbers(...numbers) {
   return numbers.map(x => x + 5);
 }
 
 const result = addFiveToABunchOfNumbers(4, 5, 6, 7, 8, 9, 10); 
 // [9, 10, 11, 12, 13, 14, 15]
 
 const [a, b, ...rest] = [1, 2, 3, 4]; // a: 1, b: 2, rest: [3, 4]
 
 const { e, f, ...others } = {
   e: 1,
   f: 2,
   g: 3,
   h: 4,
 }; // e: 1, f: 2, others: { g: 3, h: 4 }   
       

问题 13: 解释一下 Object.freeze()const 的区别

constObject.freeze是两个完全不同的概念。

const 声明一个只读的变量,一旦声明,常量的值就不可改变:

const person = {
    name: "Leonardo"
};
let animal = {
    species: "snake"
};
person = animal; // ERROR "person" is read-only    

Object.freeze适用于值,更具体地说,适用于对象值,它使对象不可变,即不能更改其属性。

let person = {
    name: "Leonardo"
};
let animal = {
    species: "snake"
};
Object.freeze(person);
person.name = "Lima"; //TypeError: Cannot assign to read only property 'name' of object
console.log(person); 

问题 14: 如何在 JS 中“深冻结”对象

如果咱们想要确保对象被深冻结,就必须创建一个递归函数来冻结对象类型的每个属性:

没有深冻结

let person = {
    name: "Leonardo",
    profession: {
        name: "developer"
    }
};
Object.freeze(person); 
person.profession.name = "doctor";
console.log(person); //output { name: 'Leonardo', profession: { name: 'doctor' } }

深冻结

function deepFreeze(object) {
    let propNames = Object.getOwnPropertyNames(object);
    for (let name of propNames) {
        let value = object[name];
        object[name] = value && typeof value === "object" ?
            deepFreeze(value) : value;
    }
    return Object.freeze(object);
}
let person = {
    name: "Leonardo",
    profession: {
        name: "developer"
    }
};
deepFreeze(person);
person.profession.name = "doctor"; // TypeError: Cannot assign to read only property 'name' of object

问题 15: 为什么要使用 ES6 类?

选择使用类的一些原因:

  • 语法更简单,更不容易出错。

  • 使用新语法比使用旧语法更容易(而且更不易出错)地设置继承层次结构。

  • class可以避免构造函数中使用new的常见错误(如果构造函数不是有效的对象,则使构造函数抛出异常)。

  • 用新语法调用父原型方法的版本比旧语法要简单得多,用super.method()代替ParentConstructor.prototype.method.call(this)Object.getPrototypeOf(Object.getPrototypeOf(this)).method.call(this)

考虑下面代码:

// **ES5**
var Person = function(first, last) {
    if (!(this instanceof Person)) {
        throw new Error("Person is a constructor function, use new with it");
    }
    this.first = first;
    this.last = last;
};

Person.prototype.personMethod = function() {
    return "Result from personMethod: this.first = " + this.first + ", this.last = " + this.last;
};

var Employee = function(first, last, position) {
    if (!(this instanceof Employee)) {
        throw new Error("Employee is a constructor function, use new with it");
    }
    Person.call(this, first, last);
    this.position = position;
};
Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Employee;
Employee.prototype.personMethod = function() {
    var result = Person.prototype.personMethod.call(this);
    return result + ", this.position = " + this.position;
};
Employee.prototype.employeeMethod = function() {
    // ...
};

使用 ES6 实现上述功能:

// ***ES2015+**
class Person {
    constructor(first, last) {
        this.first = first;
        this.last = last;
    }

    personMethod() {
        // ...
    }
}

class Employee extends Person {
    constructor(first, last, position) {
        super(first, last);
        this.position = position;
    }

    employeeMethod() {
        // ...
    }
}

问题 16: 说明下列方法为何不能用作 IIFE,要使其成为 IIFE,需要进行哪些更改?

function foo(){ }();

IIFE 代表立即调用的函数表达式。JS解析器读取函数foo(){}();作为函数foo(){}();,前者是一个函数声明,后者(一对括号)是尝试调用一个函数,但没有指定名称,因此它抛出Uncaught SyntaxError: Unexpected token 异常。

咱们可以使用void操作符:void function foo(){ }();。不幸的是,这种方法有一个问题。给定表达式的求值总是undefined的,所以如果IIFE 函数有返回值,则不能使用它,如下所示:

const foo = void
function bar() {
    console.log('前端小智')
    return 'foo';
}();

console.log(foo); // undefined

问题 17: 解释一下原型设计模式(Prototype Pattern)

原型模式会创建新的对象,而不是创建未初始化的对象,它会返回使用从原型或样本对象复制的值进行初始化的对象。原型模式也称为属性模式。

原型模式有用的一个例子是使用与数据库中的默认值匹配的值初始化业务对象。原型对象保留默认值,这些默认值将被复制到新创建的业务对象中。

传统语言很少使用原型模式,但是JavaScript作为一种原型语言,在构建新对象及其原型时使用这种模式。

问题 18: 能否比较模块模式与构造函数/原型模式的用法?

模块模式通常用于命名空间,在该模式中,使用单个实例作为存储来对相关函数和对象进行分组。这是一个不同于原型设计的用例,它们并不是相互排斥,咱们可以同时使用它们(例如,将一个构造函数放在一个模块中,并使用new MyNamespace.MyModule.MyClass(arguments) )。

构造函数和原型是实现类和实例的合理方法之一。它们与模型并不完全对应,因此通常需要选择一个特定的scheme或辅助方法来实现原型中的类。

问题 19. moduleexportimport是什么,有什么作用?##

module、export、import是ES6用来统一前端模块化方案的设计思路和实现方案。export、import的出现统一了前端模块化的实现方案,整合规范了浏览器/服务端的模块化方法,用来取代传统的AMD/CMD、requireJS、seaJS、commondJS等等一系列前端模块不同的实现方案,使前端模块化更加统一规范,JS也能更加能实现大型的应用程序开发。

import引入的模块是静态加载(编译阶段加载)而不是动态加载(运行时加载)。

import引入export导出的接口值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。

20.Iterator是什么,有什么作用

遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
Iterator 的作用有三个:

  1. 为各种数据结构,提供一个统一的、简便的访问接口;
  2. 使得数据结构的成员能够按某种次序排列;
  3. ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。

遍历过程:

  1. 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
  2. 第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
  3. 第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
  4. 不断调用指针对象的next方法,直到它指向数据结构的结束位置。

每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含valuedone两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。

//obj就是可遍历的,因为它遵循了Iterator标准,且包含[Symbol.iterator]方法,方法函数也符合标准的Iterator接口规范。
//obj.[Symbol.iterator]() 就是Iterator遍历器
let obj = {
  data: [ 'hello', 'world' ],
  [Symbol.iterator]() {
    const self = this;
    let index = 0;
    return {
      next() {
        if (index < self.data.length) {
          return {
            value: self.data[index++],
            done: false
          };
        } else {
          return { value: undefined, done: true };
        }
      }
    };
  }
};

21. Generator 函数是什么,有什么作用?

如果说 JavaScrip 是 ECMAScript 标准的一种具体实现、Iterator遍历器是Iterator的具体实现,那么Generator函数可以说是Iterator接口的具体实现方式。
执行Generator函数会返回一个遍历器对象,每一次Generator函数里面的yield都相当一次遍历器对象的next()方法,并且可以通过next(value)方法传入自定义的value,来改变Generator函数的行为。
Generator函数可以通过配合Thunk 函数更轻松更优雅的实现异步编程和控制流管理。

22.说说你对Promise的理解?

依照Promise/A+的定义,Promise 有四种状态:
pending: 初始状态, 非 fulfilled 或 rejected.
fulfilled: 成功的操作.
rejected: 失败的操作.
settled: Promise已被fulfilledrejected,且不是pending
另外, fulfilledrejected 一起合称 settled
Promise 对象用来进行延迟(deferred) 和异步(asynchronous ) 计算。
Promise 的构造函数
构造一个 Promise,最基本的用法如下:

var promise = new Promise(function(resolve, reject) {
if (...) {  // succeed
resolve(result);
} else {   // fails
reject(Error(errMessage));
}
});

问题 23:将 Symbol 引入ES6 的目的是什么?

Symbol 是一种新的、特殊的对象,可以用作对象中惟一的属性名。使用 Symbol 替换string 可以避免不同的模块属性的冲突。还可以将Symbol设置为私有,以便尚无直接访问Symbol权限的任何人都不能访问它们的属性。

Symbol 是JS新的基本数据类型。与numberstringboolean 原始类型一样,Symbol 也有一个用于创建它们的函数。与其他原始类型不同,Symbol没有字面量语法。创建它们的唯一方法是使用以下方法中的Symbol构造函数

let symbol = Symbol();    

问题 24: 在 JavaScript 中定义枚举的首选语法是什么

可以 Object.freeze 来实现枚举

var DaysEnum = Object.freeze({
    "monday": 1,
    "tuesday": 2,
    "wednesday": 3,
    ...
})

或者

var DaysEnum = {
    "monday": 1,
    "tuesday": 2,
    "wednesday": 3,
    ...
}
Object.freeze(DaysEnum)

但是,这阻止咱们把值分配给变量:

let day = DaysEnum.tuesday
day = 298832342 // 不会报错

25. Set是什么,有什么作用?

Set是 ES6 引入的一种类似Array的新的数据结构,Set实例的成员类似于数组item成员,区别是Set实例的成员都是唯一,不重复的,这个特性可以轻松地实现数组去重。

26.Map 是什么,有什么作用?

Map是 ES6 引入的一种类似Object的新的数据结构,Map可以理解为是Object的超集,打破了以传统键值对形式定义对象,对象的key不再局限于字符串,也可以是Object,可以更加全面的描述对象的属性。

问题 27: ES6 中的 WeakMap 的实际用途是什么?

WeakMaps 提供了一种从外部扩展对象而不影响垃圾收集的方法。当咱们想要扩展一个对象,但是因为它是封闭的或者来自外部源而不能扩展时,可以应用WeakMap

WeakMap只适用于 ES6 或以上版本。WeakMap是键和值对的集合,其中键必须是对象

var map = new WeakMap();
var pavloHero = {
    first: "Pavlo",
    last: "Hero"
};
var gabrielFranco = {
    first: "Gabriel",
    last: "Franco"
};
map.set(pavloHero, "This is Hero");
map.set(gabrielFranco, "This is Franco");
console.log(map.get(pavloHero)); //This is Hero

WeakMaps的有趣之处在于,它包含了对map内部键的弱引用。弱引用意味着如果对象被销毁,垃圾收集器将从WeakMap中删除整个条目,从而释放内存。

问题 28: ES6 MapWeakMap 有什么区别?

当它们的键/值引用的对象被删除时,它们的行为都不同,以下面的代码为例:

var map = new Map()
var weakmap = new WeakMap()

(function() {
    var a = {
        x: 12
    };
    var b = {
        y: 12
    };

    map.set(a, 1);
    weakmap.set(b, 2);
})()

执行上面的 IIFE,就无法再引用{x:12}{y:12}。垃圾收集器继续运行,并从 WeakMa中删除键b指针,还从内存中删除了{y:12}

但在使用 Map的情况下,垃圾收集器不会从Map中删除指针,也不会从内存中删除{x:12}

WeakMap 允许垃圾收集器执行其回收任务,但Map不允许。对于手动编写的 Map,数组将保留对键对象的引用,以防止被垃圾回收。但在WeakMap中,对键对象的引用被“弱”保留,这意味着在没有其他对象引用的情况下,它们不会阻止垃圾回收。

29. Proxy是什么,有什么作用?

Proxy是 ES6 新增的一个构造函数,可以理解为JS语言的一个代理,用来改变JS默认的一些语言行为,包括拦截默认的get/set等底层方法,使得 JS 的使用自由度更高,可以最大限度的满足开发者的需求。比如通过拦截对象的get/set方法,可以轻松地定制自己想要的key或者value。下面的例子可以看到,随便定义一个myOwnObjkey,都可以变成自己想要的函数`:

function createMyOwnObj() {
	//想把所有的key都变成函数,或者Promise,或者anything
	return new Proxy({}, {
		get(target, propKey, receiver) {
			return new Promise((resolve, reject) => {
				setTimeout(() => {
					let randomBoolean = Math.random() > 0.5;
					let Message;
					if (randomBoolean) {
						Message = `你的${propKey}运气不错,成功了`;
						resolve(Message);
					} else {
						Message = `你的${propKey}运气不行,失败了`;
						reject(Message);
					}
				}, 1000);
			});
		}
	});
}

let myOwnObj = createMyOwnObj();

myOwnObj.hahaha.then(result => {
	console.log(result) //你的hahaha运气不错,成功了
}).catch(error => {
	console.log(error) //你的hahaha运气不行,失败了
})

myOwnObj.wuwuwu.then(result => {
	console.log(result) //你的wuwuwu运气不错,成功了
}).catch(error => {
	console.log(error) //你的wuwuwu运气不行,失败了
})

30.Reflect是什么,有什么作用?

Reflect是 ES6 引入的一个新的对象,它的主要作用有两点,一是将原生的一些零散分布在ObjectFunction或者全局函数里的方法(如applydeletegetset等等),统一整合到Reflect上,这样可以更加方便更加统一的管理一些原生API。其次就是因为Proxy可以改写默认的原生API,如果一旦原生API别改写可能就找不到了,所以Reflect也可以起到备份原生API的作用,使得即使原生API被改写了之后,也可以在被改写之后的API用上默认的API

总结

希望通过这 30 道题目帮你重新回顾一下 ES6 有哪些新的知识点,下一篇,我会先分享关于 JavaScript this 到底是什么以及如何判断解决 this 指向问题,先了解决 ES5 中常见的问题,有助于我们了解 ES6 中的新特性,因为 ES6 的特性很多就是为了解决 ES5 遗留的问题,了解历史遗留原因,有助于加深我的对知识点的映像,咱们下篇见。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@大迁世界

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值