c++ 多行字符串写法_常用 ES6 特性 与 ES5 对比写法

c0f65d837ca53643c6d79f6d467e1833.png
对于 ES6 + 新特性 ,是面试中常问的基础问题,例如:箭头函数、对象、异步、模块、类等特性。所以需要非常熟悉。

review代码的过程中,我们发现比较多的 “混合” 写法导致代码可读性以及可维护性变差,代码混乱。特别是多人协作的中大型项目,代码一致性尤为重要。

对象合并Case:

// es5 写法
// 此处有更正
// API:Object.assign(target, ...sources)
// var obj3 = Object.assign(obj1, obj2)
// 更严谨的写法,感谢指正
var obj3 = Object.assign({}, obj1, obj2)

// es6 写法
const obj3 = {...obj1, ...obj2}

我们需要为团队制定代码规范,保证代码风格的一致性,同时要用工具去自动检测和自动修复,这些都是工程化中必不可少的,但更为重要的是充分 ECMAScript 的特性。

本文是对最常见特性的总结

ES1/ES2/ES3/ES5/ES6/ES7/ES8...& ES2015 / ES2016 / ES2017 / ES2018... 对于初学者来说比较容易搞懵,以下是一张 ECMAScript 发展史图:

14dde2548bce104f50217dcf2c6b394a.png

参考:https://morioh.com/p/7558b17326cb

特性

  • 箭头函数(arrow function)
  • 对象(object)
  • 异步(async / await)
  • 模块(module)
  • 类(class)

一、箭头函数 (=>)

ES5

function greetings (name) {
  return 'hello ' + name
}

ES6

// 该例子同时包含了:模版字符串的用法
// 模板字符串(template string)是增强版的字符串,用反引号(`)标识。
// 它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。

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

如果只有一个参数可以省略掉括号

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

二、操作对象(objects)

2.1 对象合并

ES5

var obj1 = { a: 1, b: 2 }
var obj2 = { a: 2, c: 3, d: 4}
var obj3 = Object.assign({}, obj1, obj2)

ES6

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

2.2 对象解构

ES5

var obj1 = { a: 1, b: 2, c: 3, d: 4 }
var a = obj1.a
var b = obj1.b
var c = obj1.c
var d = obj1.d

ES6

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

2.3 对象定义

ES5

var a = 1
var b = 2
var c = 3
var d = 4
var obj = { a: a, b: b, c: c, d: d }

ES6

// 前提是:属性名 与 变量名 一致
const a = 1
const b = 2
const c = 3
const d = 4
const obj = { a, b, c, d }

三、异步(promise / callback)

伯艺:使用Javascript如何优雅的编写异步代码​zhuanlan.zhihu.com
3242340290c5f17bd9916a81398d1c36.png

ES5

function isGreater (a, b, cb) {
  var greater = false
  if(a > b) {
    greater = true
  }
  cb(greater)
}

isGreater(1, 2, function (result) {
  if(result) {
    console.log('greater');
  } else {
    console.log('smaller')
  }
})

ES6

  • 代码结构变得更清晰,易读性更好。
  • 异常处理方式更合理。
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')
 })

ES8

ES6 的 Promise 使得 异步编程变得可读、可控。但必须使用链式方式写代码。ES8 引入了 async / await 关键字,使得可以像写同步代码一样编写异步代码:

const isGreater = (a, b) => {
 return new Promise ((resolve, reject) => {
  if(a > b) {
   resolve(true)
  } else {
   reject(false)
  }
 })
}

async function test() {
  const result = await isGreater(1, 2)
  console.log(result);
}

test()

// false

四、模块(Module)export / import

模块指的是一段可复用的代码块,在内部封装了实现细节,并对外提供一个公共API,以便其它代码可以更好的加载和使用它。

严格的来说在 ES6 / ECMAScript2016 出现之前,JavaScript 没有定义 模块的官方语法,然后社区开发者自行发明了一些定义模块的方式:

  • Asynchronous Module Definition (AMD):异步加载,依赖前置。require.js
  • Common Module Definition(CMD):异步加载,依赖就近。sea.js
  • Universal Module Definition (UMD):类似于自执行函数。browser / Node.js 都可用
  • CommonJS:一个文件一个模块,同步加载。browser / Node.js 都可用

由于Node.js 采用的是CommonJS 模块方式,也被借鉴到 浏览器端(browser)。我们通常说的 ES5 规范实际上指的就是 CommonJS 模块规范。

https://www.jvandemo.com/a-10-minute-primer-to-javascript-modules-module-formats-module-loaders-and-module-bundlers/#​www.jvandemo.com

ES5

var MyComponent = { x: 1, y: function(){ console.log('This is ES5') }}

module.exports = MyComponent;

ES6

const MyComponent = { x: 1, y: () => { console.log('This is ES6') }}

export default myModule;

ES6 提供了两种导出模块的方式

  • 命名导出(Named Export)
MyComponent.js
export const MyComponent1 = () => {
  return 'MyComponent1';
}

export const MyComponent2 = () => {
  return 'MyComponent2';
}
index.js
// 方式1
import { MyComponent1, MyComponent2 } from "./MyComponent"

// 方式2 重命名
import * as MainComponents from "./MyComponent";
// MainComponents.MyComponent1
// MainComponents.MyComponent2

// 
  • 默认导出(export default)
MyDefaultExport.js
// export
export default MyComponent = () => {
  return 'MyComponent'
}
index.js
// import
import MyDefaultComponent from "./MyDefaultExport";

两种导出方式,根据实际情况选择,如果需要导出多个值就用命名导出,否则就用默认导出。

五、类 (class)

ES6 原生支持 class 以及 extends

class

// ===============================================
// 1. 通过 class 关键词 创建类
// ===============================================

class People {
  constructor(name, age){
    this.name = name;    
    this.age = age;
  }

  sayName(){
    console.log(`hello ${this.name} !`);
  }
}

let user1 = new People('name1', 21);
user1.sayName();

extends

// ===============================================
// 2. 通过 extends 实现继承
// ===============================================
class Student extends People{
  constructor(name, age){
    super(name);  // 等同于 super.constructor(x)
    this.age = age;
  }

  sayAge() {
    console.log(`my name is ${this.name}, age is ${this.age}`);
  }
}

let stu = new Student('name1', 100);
stu.sayAge();
stu.sayName();

ES5 只能通过对象模拟实现 class

class

可以通过,构造函数、工厂模式、字面量模式、原型模式、以及 组合模式实现class
// ===============================================
// 1. 构造函数模式  [不建议]
// ===============================================
function People(name, age){
  this.name = name;
  this.age = age;

  this.toString = function(){
    console.log(`my name is ${this.name},this year ${this.age}`);
  }
}
  
var user = new People('will',21);
user.toString();


// ===============================================
// 2.工厂模式  [不建议]
// 该模式抽象了创建具体对象的过程
// 用函数来封装以特定接口创建对象的细节
// ===============================================
function People(name, age){
  var obj = new Object();
  obj.name = name;
  obj.age = age;

  obj.toString = function(){
    console.log(`my name is ${this.name},this year ${this.age}`);
  }
      return obj;
}

var user = People('will', 21);
user.toString();


// ===============================================
// 3.字面量模式  [不建议]
// 字面量可以用来创建单个对象,但如果要创建多个对象,会产生大量的重复代码
// ===============================================
var People = {
  name: 'will',
  age : 21,
  toString: function(){
    console.log(`my name is ${this.name},this year ${this.age}`);
  }
}
People.toString();

// ===============================================
// 4.原型模式  [不建议]
// 使用原型对象,可以让所有实例共享它的属性和方法
// ===============================================
function People(){
  People.prototype.name = "will";
  People.prototype.age = 21;
  People.prototype.toString = function(){
    console.log(`my name is ${this.name},this year ${this.age}`);
  }
}

var user1 = new People();
user1.toString();
var user2 = new People();
user2.toString();
console.log(user1.toString == user2.toString); // true

// isPrototypeOf:检测,是否是该对象的实例对象
console.log(People.prototype.isPrototypeOf(user1)); // true
console.log(People.prototype.isPrototypeOf(user2)); // true
console.log(user1.__proto__ === People.prototype) // true

// hasOwnProperty:  检测 属性是否 存在实例中
console.log(user1.hasOwnProperty('name')); // false

// ===============================================
// 5.组合模式   [最佳实践]
// 最常见的方式
// 构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性
// 这种组合模式还支持向构造函数传递参数。实例对象都有自己的一份实例属性的副本
// 同时又共享对方法的引用,最大限度地节省了内存。
// 该模式是目前使用最广泛、认同度最高的一种创建自定义对象的模式
// ===============================================

function People(name, age){
  this.name = name;
  this.age = age;
  this.friends = ["a","b"];
}

People.prototype = {
  toString : function(){
      console.log(`my name is ${this.name},this year ${this.age}`);
  }
}

var user1 = new People("name1", 10);
var user2 = new People("name2", 20);

console.log(user1.friends); // ["a", "b"]
user1.friends.push('c');

console.log(user1.friends); // ["a", "b", "c"]
console.log(user2.friends); // ["a", "b"]

user1.toString(); // my name is name1,this year 10
user2.toString(); // my name is name2,this year 20

console.log(user1.friends === user2.friends); // false
console.log(user1.toString === user2.toString); // true

extends

通过,原型链、借用构造函数、组合继承、原型式继承、寄生式继承、寄生组合式继实现 extends
// ===============================================
// 1. 原型链  [不建议]
// 问题1:对于包含引用类型值的原型属性,会被所有实列共享,所以我们更希望在构造函数中定义属性
// 问题2:在创建自类型实例时,不能给父类型传参
// ===============================================
function Super() {
  this.super_age = 50
}
Super.prototype.getSuperAge = function() {
  return this.super_age;
}

function Sub() {
  this.sub_age = 20
}
Sub.prototype = new Super();
Sub.prototype.getSubAge = function() {
  return this.sub_age;
}

var instance = new Sub();
console.log(instance.getSuperAge()); // 50

// instanceof:判断实例
// isPrototypeOf: 判断实例
console.log(instance instanceof Super) // true
console.log(instance instanceof Sub) // true

// ===============================================
// 2. 借用构造函数 [不建议]
// 通过调用 call() 和 apply(),实现在 Sub 实例环境下,调用 Super 构造函数,同时可以传递参数
// 存在问题:方法都在构造函数中定义,无法复用。
// ===============================================
function Super(name) {
    this.name = name;
    this.colors = ['red', 'blue'];
}

function Sub(name) {
    Super.call(this, name);
    this.age = 20;
}

var instance1 = new Sub('name1');
instance1.colors.push('yellow');
console.log(instance1.colors); // ['red', 'blue', 'yellow']

var instance2 = new Sub('name2');
console.log(instance2.colors); // ['red', 'blue']


// ===============================================
// 3. 组合继承  [伪经典继承]  [常用]
// 原型(原型属性 + 方法 的继承) + 构造函数(实例属性的继承)
// 问题:调用两次 超类型构造函数(创建子类 +  子类构造函数内部)
// ===============================================
function Super(name) {
    this.name = name;
    this.colors = ['red', 'blue'];
}
Super.prototype.sayName = function() {
    console.log(this.name);
}

function Sub(name, age) {
    Super.call(this, name);
    this.age = age;
}
Sub.prototype = new Super();
Sub.prototype.sayAge = function() {
    console.log(this.age);
}


// ===============================================
// 3.原型式继承
// 问题:引用类型属性共享
// ===============================================
var person = {
   name: 'p1',
   friends: ['a', 'b', 'c']
}

var otherPerson1 = Object.create(person);
otherPerson1.name = 'p2';
otherPerson1.friends.push('d');

var otherPerson2 = Object.create(person);
otherPerson2.name = 'p3';
otherPerson2.friends.push('e');
console.log(otherPerson2.friends);  // ['a', 'b', 'c', 'd']


// ===============================================
// 4.寄生式继承
// 创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再返回该对象
// ===============================================
function createAnother(original) {
    var clone = object(original);  // 创建新对象
    clone.sayHi = function() {   // 增强这个对象
        console.log('Hi'); 
    }
    return clone;  // 返回 增强后的 对象
}

 
// ===============================================
// 5.寄生组合式继承
// 问题:引用类型属性共享
// ===============================================
function Super(name) {
    this.name = name;
    this.colors = ['red', 'blue'];
}
Super.prototype.sayName = function() {
    console.log(this.name);
}


function Sub(name, age) {
    Super.call(this, name);
    this.age = age;
}
Sub.prototype = new Super();
// 借用构造函数:继承属性
// 原型链的混成形式:继承方法
// 基本思路:不必为了指定子类型的原型 而 调用父类型的构造函数。我们需要的是一个父类型原型的副本
// 本质:使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。
function inheritPrototype(Super, Sub) {
    var prototype = Object(Super.prototype); // 创建对象
    prototype.constructor = Sub; // 增强对象
    Sub.prototype = prototype;  // 指定对象
}
inheritPrototype(Super, Sub);
Sub.prototype.sayAge = function() {
    console.log(this.age);
}

参考:

https://medium.com/@etherealm/named-export-vs-default-export-in-es6-affb483a0910​medium.com ES6 vs ES5 - Aaludra Technologies​aaludra.com
60541dee0fb33ac2b5804fd6a041d6c8.png
https://medium.com/recraftrelic/es5-vs-es6-with-example-code-9901fa0136fc​medium.com javascript中的面向对象​mp.weixin.qq.com
afe83c7a30992f80c04cc2a7c3f430be.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值