JavaScript的几个概念简单理解(深入解释见You Don't know JavaScript这本书)

ES201X是JavaScript的一个版本。

 

ES2015新的feature

  • let, const
  • Scope, 块作用域
  • Hoisting
  • Closures
  • DataStructures: Objects and Arrays
  • this

 


 

let, const, Block Scope

 

新的声明类型let, const,配合Block Scope。(if, forEach,)

之前:

var,  Global scope和function scope。

之后:

let, const , 这2个类型声明用在Block Scope内。

 

声明一次还是多次?

let, const只能声明一次。但var声明可以反复声明:

const num = 5;
const num = 7; // SyntaxError: identifier 'num' has already been declared

var x = 2; // x = 2
var x = 4; // x = 4

 

⚠️const 用于不会改变的值。

⚠️const x = {} , 可以改变其内的属性值,或增加新的key/value对儿。

这是因为,const variables当处理arrays 或objects时会改变,

技术上讲,不是re-assign分配一个新的值给它,而是仅仅改变内部的元素。

const farm = [];
farm       = ['rice', 'beans', 'maize'] // TypeError: Assignment to constant variable

//但可以
farm.push('rice')

 

Hoisting

Declarations will be brought to the top of the execution of the scope!

⚠️ var x = "hello" 这种声明加分配assign的写法无法Hoisting!!

⚠️ 函数声明也可以Hoisting.

 


Closures

lexical or function closures.

用于把函数和周围的state(var, const, let声明的变量,函数。)绑定一起使用。

换句话说,closure给你入口进入到函数作用域外面的区域,并使用这个区域的变量,函数。

function jump() {
  var height = 10;

  function scream() {
    console.log(height);
  }

  return scream;
}

var newJump = jump()  //function runs but doesnot log anything

newJump();  //logs 10

在函数jump中声明了height, 它只存在于函数作用域内。

通过Closure, 函数scream得到了height。

执行函数jump, 并把函数scream存入一个新的变量newJump, 这个newJump就是一个函数

执行newJump(), 就是执行scream。 而scream可以引用jump内的height。

JavaScript所做的是保持一个对原始作用域的引用,我们可以使用它和height变量

这个引用就叫做closure。

 

另一个例子:

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

// use
var addUp7  = add(7)
var addUp14 = add(14)

console.log (addUp7(8)); // 15
console.log (addUp14(12)); // 26

addUp7和addUp14都是closures。

例如:

addup7创造了一个函数和一个变量a。a的值是7。

执行addUp7(8), 会返回a + 8的结果15。

 

为什么要使用闭包closures?

开发者总是寻找更简洁和高效的编码方式,来简化日常工作和让自己更多产。

理解和使用closures就是其中的一个技巧。使用closures有以下好处:

1. Data Encapsulation 

closures可以把数据储存在独立的scope内。

例子:

在一个函数内定义一个class

//使用(function(){}())在函数声明后马上执行,需要用()括起来。
(function () {
  var foo = 0
  function MyClass() {
    foo += 1;
  };
  MyClass.prototype = {
    howMany: function() {
      return foo;
    }
  };
  window.MyClass = MyClass;
}());

foo可以通过MyClass constructor存入和howMany方法取出。

即使IIFE方法被执行后退出, 变量foo仍然存在。MyClass通过closure功能存取它的值。

 

2.Higher Order Functions

简化多层嵌套的函数的代码。

//x从它被定义的位置被一步步传递到它被使用的位置。
 function bestSellingAlbum(x) { 
   return albumList.filter(
     function (album) { return album.sales >= x; }
   );
 }

filter自动获取了x,这就是闭包的作用。

如果没有closures, 需要写代码把x的值传递给filte方法。

 

现在使用ES6, 代码就更简单了。

const bestSellingAlbums = (x) => albumList.filter(album => album.sales >= x);

 

 

Closures的实践

1.和对象化编程一起使用

//声明一个函数对象
function count() {
  var x = 0;
  return {
    increment: function() { ++x; },
    decrement: function() { --x; },
    get: function() { return x; },
    reset: function() { x = 0; }
  }
}

 

执行这个函数

var x = count();
x.increment() //返回1
x.increment() //返回2
x.get() //返回2
x.reset() //重置内部变量x为0

  

2.传递值和参数进入一个算法。

function proximity_sort(arr, midpoint) {
    return arr.sort(function(x, y) { x -= midpoint; y -= midpoint; return x*x - y*y; });
}

 

3. 类似namspace的作用。

 var houseRent = (function() {
   var rent = 100000;
   function changeBy(amount) {
     rent += amount;
   }
   return {
     raise: function() {
       changeBy(10000);
     },
     lower: function() {
       changeBy(-10000);
     },
     currentAmount: function() {
       return rent;
     }
   };
 })(); 

houseRent可以执行raise, lower, currentAmount函数。但不能读取rent,changeBy函数。

 

Closures在函数编程中的2个重要concepts(不理解?)

  • partial application 局部应用
  • currying 梳理(?)

Currying是一种简单的创建函数的方法,这种方法会考虑一个函数的参数的局部使用。

简单来说,一个函数接受另一个函数和多个参数,并返回一个函数及较少的参数。

Partial application搞定了在返回函数中的一个或多个参数。

返回的函数接受了剩下的parameters作为arguments来完成这个函数应用。

    partialApplication(targetFunction: Function, ...fixedArgs: Any[]) =>
      functionWithFewerParams(...remainingArgs: Any[])

 

使用array的普通方法的内部结构来理解这2个概念:

Array.prototype.map()方法

map用来创建一个新的array。 调用一个函数作为参数,这个函数应用到每个array元素上,并返回结果。

var new_array = arr.map(function callback(currentValue[, index[, array]]) {
    // Return element for new_array
}[, thisArg])

 

参数Parameters:

  • callback:函数,用于创建新array的元素,它接受3个参数,第一是必须的:
    • currentValue: 在array中,正在被执行的元素。
    • index:  current element在array 中的索引。
    • array: 调用map方法的数组本身。
  • thisArg: 当执行callback时,作为this本身。

Return value:

一个新的array,每个元素是被callback函数处理过的。

内部结构:

 Array.prototype.map = function(callback) {  
   arr = [];
   for (var i = 0; i < this.length; i++)
     arr.push(callback(this[i],i,this));
   return arr;
 };

 

Array.prototype.filter()方法

和map结构类似,但是,callback函数用来测试每个数组中的元素。如果返回ture则保留这个元素。

 

 


 

Data Structures: Objects and Arrays

相比String, Number, Boolean, Null, Undefined更复杂的数据结构。可以看作是其他数据结构的容器。

一般来说,几乎everything in JS is an Object. 因此, JS可以看成是对象化编程语言。

在浏览器中,一个window加载后,Document对象的一个实例就被创建了。

在这个页面上的所有things都是作为document object的child。

Document methods是用来对父对象进行相关的操作。

const myObject = {
    myKey1: 'ObjectValue1',
    myKey2: 'ObjectValue2',
    mykeyN: 'ObjectValueN',
    objectMethod: function() {
      // Do something on this object
    }
};

 

 

为什么说everything is an Object?

const myMessage = "look at me!"这个数据类型是字符串。

但其实它是由String对象生成的实例instance,通过_proto_属性,可以调用String中的methods.

String.prototype.toUpperCase.

myMessage.toUpperCase()  

⚠️:

myMessage是String的一个实例,myMessage的_proto_属性,就是String.prototype。

实例myMessage通过_proto_属性,调用String中储存在prototype上的方法。

// simple way to create a string
const myMessage = 'look at me go!';

// what javascript sees
const myOtherMessage = String('look at me go!');

myMessage == myOtherMessage; // true

扩展知识点:见博客:原型对象和prototype(https://www.cnblogs.com/chentianwei/p/9675630.html)

 

constructor属性

还是说上面的例子,String实例myMessage的数据类型是什么:

typeof myOtherMessage
//返回 "string"

 

这是如何构建的?使用实例的constructor属性:

myOtherMessage.constructor
//得到,ƒ String() { [native code] }

 

 

由此可知myOtherMessage变量用于存储一个字符串。它本身是由String构造的实例对象。

everything is object,理解了把!


 

"this" Keyword

详细看(博客)(或者YDJS对应的章节)
4条rule:

  1. default binding
  2. implicit binding
  3. explicit binding:  使用call(), apply(),bind()方法明确指定call-site
  4. new binding

使用方法:

先找到call-site,然后检查符合哪条rule。

call-site,即一个函数被调用的地点,也可以说一个函数所处的execution context。

 

优先级:

new > explicit > implicit > default

 

Rule 1: Default (Global) Binding

  1. 默认使用全局作用域。
  2. 如果在函数中使用'use strict'模式,this的作用域是这个函数的作用域。

 

Rule 2: Implicit Binding

根据call-site。根据对象。

例子:

var company = {
  name: 'Baidu',
  printName() {
     console.log(this.name)
  }
}

var name = 'google'

company.printName()  // Baidu,  因为printName()函数被company对象调用。

var printNameAgain = company.printName

printNameAgain(); // google,    因为这个函数被全局对象调用。

 

 

Rule 3: Explicit Binding

可以明确指定调用点。

使用call, apply ,bind方法。传递对象作为参数。第一个参数是一个对象,它被传递给this关键字。

因此this指向就明确了。

//根据上例子

printNameAgain.call(company)   //  Baidu.

 

call(), 可以传递string, 数字等作为其他参数。

apply(), 只能接受一个数组作为第2个参数.

 

bind()最为特殊,它会绑定一个context。然后返回这个函数和调整后的context。

var printFunc = printNameAgain.bind(company)
//printFunc是printNameAgain函数,并绑定company对象, this永远指向company。
printFunc()  // Baidu

 

 

Rule 4: Constructor Calls with new

使用函数构造调用。如new Number()

注意:

constructor就是一个函数,和new操作符一起在被调用时运行。和类无关也不实例化类!就是一个标准的函数。

当一个函数前面有一个new, 会自动做以下事情:

  1. a brand new object is created
  2. the newly constructed object is [[Prototype]]-linked,
  3. the newly constructed object is set as the this binding for that function call
  4. unless the function returns its own alternate object, the new-invoked function call will automatically return the newly constructed object

第3句:新构建的对象被设置为this, 这个对象和new-invoked函数调用绑定在一起。

function Company() {
  this.name = 'baidu'
}

var  b  = new Company()

//当执行代码时,会发生:
function Company() {
  // var this = {};
  this.name = 'Scotch'
  // return this;  给变量b。
}

 

由此可知:

第4句:默认情况下,使用new关键字的函数,会自动地返回新构建的对象。除非明确指定返回对象。

 

Async Handling

在异步逻辑时,函数回调可能存在this binding的陷阱。

因为这些handers往往绑定一个不同的context,让this的行为产生非期待的结果。

 

Event handing就是一个例子。事件处理器是回调函数。在运行时,当事件被激活,回调函数被执行。

浏览器需要提供和这个event相关的contextual information。它会绑定到回调函数中的this关键字。

 

以下代码,在执行后可以看到this的绑定对象:

button.addEventListener('click', function() {
  console.log(this)
});

 

 

假如你希望产生某个行为:

var company = {
  name: 'Scotch',
  getName: function() {
    console.log(this.name)
  }
}

// This event's handler will throw an error
button.addEventListener('click', company.getName)

 

因为浏览器提供的contextual info内,不一定有name这个属性。所以可能会报告❌。

告诉你name property is not existed。

或者有这个name属性,但返回的值肯定不是你想要的结果。

所以需要使用bind()方法,明确指定this的绑定:

button.addEventListener('click', company.getName.bind(company))

 



 

 

Basic Deisgn Patterns 

软件工程设计,存在大量不同的设计模式。

JS是一种非传统的面向对象的编程语言。

 

设计模式(全免介绍不同的JS设计模式)

(https://www.dofactory.com/javascript/design-patterns)

 

3种设计模式分类: 

Creational patterns:

这种模式集中在创建对象。当在一个大型程序种创建对象时,有让对象变复杂的趋向。通过控制对象的创建,Creational design patterns创造式设计模式可以解决这个问题。

 

Structural patterns:

这种模式提供了方法,用于管理对象和对象的关系,以及创建class structure。

其中一种方法是通过使用inheritance继承, 和composition,从多个小的对象,到创建一个大的对象。

 

Behavioral patterns

这种模式集中在对象之间的交互上。

 

区别:

  • Creational patterns描述了一段时间。
  • Structural patterns描述了一个或多或少的static structure。
  • 而Behavioral patterns描述了一个process, a flow。

 

 

Creational Patterns

Module模块

这种模式常用于软件开发,这个module pattern可以被看作 Immediately-Invoked-Function-Expression (IIFE).

(function() {

  // code goes here!

})();

 

所有module代码在一个closure内存在。

Variables通过执行函数传入value来进口imported, 并返回一个对象来出口exported。

 

这种Modules比单独的函数使用的方法有优势:

它能够让你的global namespace 更干净,更可读性,也让你的函数可以importable and exportable.

一个例子:

const options = {
  username: 'abcd',
  server:   '127.0.0.1'
};

const ConfigObject = (function(params) {

  // return the publicly available things
  // able to use login function at the top of this module since it is hoisted
  return {
    login: login
  };

  const username = params.username || '',
    server       = params.server || '',
    password     = params.password || '';

  function checkPassword() {
    if (this.password === '') {
      console.log('no password!');
      return false;
    }

    return true;
  }

  function checkUsername() {
    if (this.username === '') {
      console.log('no username!');
      return false;
    }

    return true;
  }

  function login() {
    if (checkPassword() && checkUsername()) {
      // perform login
    }
  }

})(options);

 

 

Builder

这种模式的典型就是jQuery,虽然现在不再使用它了。

const myDiv = $('
This is a div.
'); // myDiv now represents a jQuery object referencing a DOM node. const myText = $('

'); // myText now represents a jQuery object referencing an HTMLParagraphElement. const myInput = $(''); // myInput now represents a jQuery object referencing a HTMLInputElement

 

这种模式,让我们构建对象而无需创建这个对象,我们需要做的是指定数据type和对象的content。

这种模式的关键:

It aims at separating an object’s construction from its representation

我们无需再设计construction了。只提供内容和数据类型即可。

 

 $ variable adopts the Builder Pattern in jQuery.

因此,无需再使用如document.createElement('div')等传统的创建node的method。

 

除了Module和Builder,Creational Patterns还有Factory Method, Prototype, Singleton

Abstract Factory Creates an instance of several families of classes Builder Separates object construction from its representation Factory Method Creates an instance of several derived classes Prototype      A fully initialized instance to be copied or cloned Singleton      A class of which only a single instance can exist    

 


 

Structural Patterns

Facade

A single class that represents an entire subsystem

这种模式:简单地把一大片逻辑隐藏在一个简单的函数调用中function call。

内部的子程序和layers也被隐藏,并通过a facade来使用。

这种模式让开发者看不到它的内部结构。开发者也无需关心它。

例子:

$(document).ready(function() {

  // all your code goes here...

});

 

Composites

Composites are objects composed of multiple parts that create a single entity. 

A tree structure of simple and composite objects

例子: 

$('.myList').addClass('selected');
$('#myItem').addClass('selected');

// dont do this on large tables, it's just an example.
$('#dataTable tbody tr').on('click', function(event) {
  alert($(this).text());
});

 

 

其他模式:

  Adapter    Match interfaces of different classes
  Bridge    Separates an object’s interface from its implementation
  Composite    A tree structure of simple and composite objects
  Decorator    Add responsibilities to objects dynamically
  Facade    A single class that represents an entire subsystem
  Flyweight    A fine-grained instance used for efficient sharing
  Proxy    An object representing another object

 

 


 

 

Behavioral patterns

Observer

The observer design pattern implements a single object which maintains a reference to a collection of objects and broadcasts notifications when a change of state occurs. When we don’t want to observe an object, we remove it from the collection of objects being observed.

 

Vue.js对Vue实例中 data属性的追踪,应该是一个Observer pattern。

 

结论:

没有完美的设计模式。各种模式都是为了更好的写web app。

相关书籍:

"Learning JavaScript Design Patterns" by Addy Osmani 


 

 

Callbacks, Promises, and Async

 

函数是First-Class Objects:

1.函数可以被当作value,分配给变量

2.可以嵌套函数

3.可以返回其他函数。

 

Callback Functions 

当一个函数简单地接受另一个函数作为参数argument, 这个被当作参数的函数就叫做回调函数。

使用回调函数是函数编程的核心概念。

例子:

setInterval(),每间隔一段time, 调用一次回调函数。

setInterval(function() {
  console.log('hello!');
}, 1000);

var intervalID = scope.setInterval(func, delay[, param1, param2, ...]);

 

例子:

Array.map(), 把数组中的每个元素,当作回调函数的参数,有多少元素,就执行多少次回调。最后返回一个新的array.

 

Naming Callback functions

回调函数可以被命名,回调函数可以是异步的。setInterval()中的函数就是异步函数。

function greeting(name) {
    console.log(`Hello ${name}, welcome to here!`)
}

function introduction(firstName, lastName, callback) {
    const fullName = `${firstName} ${lastName}`
    callback(fullName)
}
introduction(
'chen', 'ming', greeting) // Hello chen ming, welcome to here!

 

当执行introduction()时,greeting函数被当作参数传入,并在introduction内部执行。

 

回调地狱Callback hell

function setInfo(name) {
  address(myAddress) {
    officeAddress(myOfficeAddress) {
      telephoneNumber(myTelephoneNumber) {
        nextOfKin(myNextOfKin) {
          console.log('done'); //let's begin to close each function! 
        };
      };
    };
  };
}

 

除了不好看外,当变复杂后,传统的回调函数调用还会出现回调函数调用过早,或者过晚,等等问题

具体看这篇博客https://www.cnblogs.com/chentianwei/p/9774098.html

 

Promises

因为Promises封装了时间依赖状态the time-dependent state ---等待满足或拒绝这个潜在的value--从外部,因此Promises可以被composed(combined, 比如x和y做加法运算)

一旦一个Promise被解决,它就是一个不变的值,如果需要可以多次被observed。

 

Promises是一个方便的可重复的机制用于封装和组织furture value。

 

Promise有3个状态:

  • Pending: 初始状态,在一个操作开始前的状态.
  • Fulfilled: 当指定的操作被完成后的状态。
  • Rejected: 操作未完成,或者抛出一个❌值
const promise = new Promise(function(resolve, reject) {
   //相关代码
   //resole, reject是2个Promise的内置函数。处理不同的结果。
})

 

 

var weather = true
const date    = new Promise(function(resolve, reject) {
  if (weather) {
    const dateDetails = {
      name:     'Cubana Restaurant',
      location: '55th Street',
      table:    5
    };

    resolve(dateDetails)
  } else {
    reject(new Error('Bad weather, so no Date'))
  }
});

 

date的值是一个Promise对象,其中:

属性[[PromiseStatus]]的值是"resolved",

(如果weather = false, 最后date中的属性[[PromiseStatus]]的值是"rejected"

属性[[PromiseValue]]的值是一个对象.

JavaScript的几个概念简单理解(深入解释见You Don't know JavaScript这本书)_第1张图片

 

使用Promise对象

当得到promise对象后,可以使用then(), catch(),调用回调函数,

对promise对象进行进一步操作,并返回一个新的promise对象。

当Promise对象中的属性[[PromiseStatus]]的值:

  • resolve,会调用then()
  • rejecte,   会调用catch().

例子:

const weather = false
const date    = new Promise(function(resolve, reject) {
  if (weather) {
    const dateDetails = {
      name:     'Cubana Restaurant',
      location: '55th Street',
      table:    5
    };

    resolve(dateDetails)
  } else {
    reject('Bad weather, so no Date')
  }
});

date
.then(function(done) {
//
})
.catch(function(err){console.log(err)})
//最后输出
'Bad weather, so no Date'
 
   

 

我们使用?创建的promise对象date(weather变量的值是true, 返回resolve(dateDetails)。)

const myDate = function() {
    date
        .then(function(done) {
            console.log("We are going on a date!")
            console.log(done)
        })
        .catch(function(err) {
            console.log(err)
        })
}
myDate()

 

得到结果:

We are going on a date!
// console.log(done) 在控制台界面显示done参数。
{name: "Cubana Restaurant", location: "55th Street", table: 5}
    location: "55th Street"
    name: "Cubana Restaurant"
    table: 5
    __proto__: Object

 

由此可见,.then()中,给回调函数的参数done,其实就是promise对象date中的[[PromiseValue]]

而date是执行resolve(dateDetails)函数后返回的结果。

因此,.then()接收的参数就是promise对象date的resolve()的值。

而,  .catch()接收的参数是promise对象date的reject()的值。

 

⚠️Promises是异步的。Promises在函数中被放置在一个小的工作栈内,并在其他同步操作完成后运行run。

 

Chaining Promises

如果基于the result of preceding promises来执行2个以上的异步操作时,需要chain promises。

还是使用上面的代码:

//创建一个新的promise
const order = function(dateDetails) {
  return new Promise(function(resolve, reject) {
    const message = `Get me an ASAP to ${dateDetails.location}, We are going on a date!`
    resolve(message)
  })
}

 

简化写法:

const order = function(dateDetails) {
  const message = `Get me an Asap to ${dateDetails.location}, We are going on a date`
  return Promise.resolve(message)
}

然后,我们就可以chain连接之前写的Promise对象date了,

这是因为执行order函数会返回一个新的promise对象。

const myDate = function() {
  date
  .then(order)  //传入回调函数order
  .then(function(done) { //...})
  .catch(function(err) { console.log(err.message)})
}
myDate()

 


 

 

Async and Await (点击查看文档说明)

EMS2017中的新语法糖。仅仅让写promise更容易。但老的浏览器不支持。

在一个函数前加上async关键字,这个函数会返回一个promise对象。

如果函数返回的值是true, promise中的[[PromiseState]]的值将是'resolved'。

但是函数返回的值是false, async function会抛出❌,promise中的[[PromiseState]]的值将是'rejected'

async function myRide() {
  return '2017'
}

 等同于

function yourRide() {
  return Promise.resolve('2017')
}

如果函数返回false:

function foo() {
  return Promise.reject(25)
}
// is equal to
async function() {
  throw 25
}

 

Await

和async function配套使用的关键字(语法糖)

用于保证所有在async function中返回的promises是同步的。

await等待其他promise执行完成,然后在执行后续代码。

 

async在返回一个promise前是等待状态。

await在调用一个promise前是等待状态。

 

async function myDate() {
    try {
        let dateDetails = await date;
        let message = await order(dateDetails)
        console.log(message)
    } catch(err) { 
        console.log(err.message)
    }
}

(async () => {
  await myDate();
})()

 

 


 

回顾

Es6是Js的核心版本,每个js developer必须掌握的。

  1. Promises  
  2. Variable Declaration 
  3. Multi-line Strings  使用反引号``
  4. Destructuring Assignment  
  5. Object Literals  
  6. Arrow Function  
  7. Default Parameters  
  8. Template Literals  在反引号内,使用${ //... }
  9. Classes  
  10. Modules  

 

 

Promises in ES6

在promise出现之前,开发者不得不大量使用callbacks。

setTimeout, XMLHttpRequest是基本的基于浏览器的异步函数的回调。

setTimeout(function() {
  console.log('Cool!')
}, 1000);

 

使用Promise:

let lunchTime = new Promise(function(eat, skip) {
  setTimeout(eat, 1000)
}).then(function() {
  console.log('lunch time!')
})

 

使用ES6箭头函数更方便:

let lunchTime = new Promise((eat, skip) => setTimeout(eat, 1000))
    .then(() => console.log('Lunch Time!'))

 

随着应用开发,代码的逻辑会更加的复杂,promise可以更好的处理这些逻辑:

//声明的变量储存一个函数
var lunchTimeDelay1000 = () => new Promise((eat, skip) => { setTimeout(eat, 1000) })

//使用2次.then()执行不同的逻辑:
//第一个then()内,返回lunchTimeDelay1000的执行,即返回原promise对象,以便执行下一个逻辑
lunchTimeDelay1000()
  .then(function() {
    console.log('Lunch Time!');
    return lunchTimeDelay1000();
  })
  .then(function() {
    console.log('Good Food!');
  });

如此,代码非常容易理解,运行代码,等待1秒,然后继续运行代码,再等待,并可以再运行更多代码。

 

Variable Declaration in ES6

新版使用let, const, var的使用范围被减少。

 

Multi Line Strings in ES6

传统字符串,换行用\n, 叫做backslash

ES6,可以使用backticks,反引号 ``, 然后直接回车换行。

const drSeuss = `
  My name is Sam I Am
  I do not like green eggs and ham
  Lunchtime is here. Come and eat
`;

等同于:

const drSeuss = 'My name is Sam I Am,\n' + 'I do not like green eggs and ham,\n' + 'Lunchtime is here. Come and eat'

 

称为:ES6 template strings,模版字符串。

template strings的另一个功能,可以插入变量, 使用${}

 

Destructuring Assignment in ES6  拆解赋值

见对象x:

var x = {fish: 1, meat: 2}
fish = x.fish  //1
meat = x.meat // 2

//使用ES6
var {fish, meat} = x

 

 注意⚠️,变量fish, meat,这2个identifier,在x中有对应的属性fish和meat。如果没有,则无法赋值。

 

array也可以拆分分配

具体见之前的博客:https://www.cnblogs.com/chentianwei/p/10153866.html

例子:

for..of显示一个array的内容

var arr = [
  ["a", 1],
  ["b", 2],
  ["c", 3]
]

for (let [key, value] of arr) {
    console.log(`${key}: ${value}`)
}

//输出
a: 1
b: 2
c: 3

 

 

Object Literals in ES6

object Literals的一些简化和改进:

  • 在object construction上建立prototype (未理解用途)
  • 简化方法声明
  • Make super calls  (未理解用途)
  • Computed property names

例子:典型的ES5对象字面量,内有一些方法和属性。这里使用了ES6的模版string功能:

var objectManager = {
  port: 3000,
  url:  'manage.com'  
};

const getAccounts = function() {
  return [1, 2, 3];
}

var manageObjectES5 = {
  port:        objectManager.port,
  url:         objectManager.url,
  getAccounts: getAccounts,
  toString: function() {
    return JSON.stringify(this.valueOf());
  },
  getUrl: function() {
    return `http://${this.url}:${this.port}`;
  },
  valueOf_1_2_3: getAccounts();
};

 

使用ES6的功能:

const manageObject = {
  proto: objectManager,
  getAccounts,
  // Also, we can invoke super and have dynamic keys (valueOf_1_2_3):
  toString() {
    return JSON.stringify((super.valueOf()))
  },
  getUrl() {
    return "http://" + this.proto.url + ':' + this.proto.port
  },
  ['valueOf_' + getAccounts().join('_')]: getAccounts()
};

getAccounts, 引用一个函数,只要名字相同就可以简化方法声明。

super关键字

用于存取和调用对象的父对象上的函数。

super.prop, super[expr]表达式可以通过任何在classes和object literals中的方法定义的验证。

super([arguments]); // calls the parent constructor.
super.functionOnParent([arguments]);

['valueOf_' + getAccounts().join('_')]: 可以使用通过计算得到的属性名字。

 

Arrow Functions in ES6

箭头函数中的this.指向调用函数的对象。

const stringLowerCase = function() {
  this.string = this.string.toLowerCase();
  return () => console.log(this.string);
}

stringLowerCase.call({
  string: 'JAVASCRIPT'
})()

 

注意,只有一行的代码的时候,使用箭头函数,可以省略return

let sayHello = () => 'hello'

 

 

Default Parameters in ES6

小的功能的改进

var garage = function(model, color, car) {
  var model = model || "Mustang"
  var color = color || 'blue'
  var car = car || 'Ford'
}

//ES6
var garage = function(model = 'Mustang', color = 'blue', car = 'Ford') {
  // ...
}

 

 

Template Literals in ES6

var x =10
var y = 20
console.log(`Thirty is ${x + y}`)
`\`` === '`' // --> true

 

在backticks反引号内使用dollar sign and curly braces: ${ //... }

 

Nesting templates

可以在一个template内使用``, 即嵌套模版。

var classes = 'header'
classes += (isLargeScreen() ?
   '' : item.isCollapsed ?
     ' icon-expander' : ' icon-collapser');

//使用ES6的template,但不嵌套
const classes = `header ${ isLargeScreen() ? '' :
    (item.isCollapsed? 'icon-expander : 'icon-collapser'')
}`

//使用嵌套模版,这里的作用是把'icon'字符串提炼出来。(这里只是演示,用途不大)
const classes = `header ${ isLargeScreen() ? '' :
 `icon-${item.isCollapsed ? 'expander' : 'collapser'}` }`;

 

 


 

Classes in ES6

在Js的存在的prototype-based 继承上的语法糖。

简化了传统的写法。可以像传统的对象类一样,使用class句法。但核心未变(prototype-based inheritance)。

 

定义classes

classes只不过是特殊的函数。

就像可以定义函数表达式和函数声明。class句法也有2个组件:

  •  class expressions:是在ES6中定义一个类的一个方法,还可以类声明。
  •  class declarations:  用一个名字来创建一个新的class。用到prototype-based inheritance. 
//class expression
var MyClass = class [className] [extends] {
  // class body
}

//class declaration
class name [extends] {
  // class body
}

 

 

class expressions

和class statement(declaration)有类似的句法结构。但是,

  1. 可以omit name,漏掉name
  2. 可re-define/re-declare classes, 并不会抛出❌。(和ruby中定义类一样)
  3. 使用typeof检查它的类型,永远是function类型。

如果一个类表达式使用一个name,这个name会存在类表达式的body内。

class declarations

只能定义一次,之后不能再修改。

注意,类声明不能被hoisting。所以必须先声明,再使用。否则会报错❌。

 

Class body and method definitions

所有的类成员都写在body内,即{ }内。如methods or constructor。

Constructor

这是一个特殊的方法,用于创建和初始化initializing一个对象。

一个类内只能有一个constructor方法。否则抛出❌。

Prototype methods

从ES6开始,关于对象初始化的方法定义的简单句法被引进。

var obj = {
  property(params...) {},
  *generator(params...) {},
  async property(params...) {},
  async *generator(params...) {},

  //使用计算的keys
  [property](params...) {},  
  *generator(params...) {},
  async property(params...) {},

  //使用比较句法 getter/setter, 定义一个非函数属性。
  get property() {}
  set property() {}
}

 

The shorthand syntax is similar to the getter and setter syntax introduced in ECMAScript 2015.

例子:

var bar = {
    foo0() { return 0 },
    foo1() { return 1 },
    ['foo' + 2]() { return 2}
}

 

 

Instance properties

实例属性必须定义在class methods内。

class Rectangle {
  constructor(height, width) {    
    this.height = height;
    this.width = width;
  }
}

 

Static methods

static关键字定义了静态方法。调用静态方法无需实例化类,也不能被类实例调用(就是类方法)

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  static distance(a, b) {
    const dx = a.x - b.x;
    const dy = a.y - b.y;

    return Math.hypot(dx, dy);
  }
}

const p1 = new Point(5, 5);
const p2 = new Point(10, 10);

console.log(Point.distance(p1, p2)); 

  

类的继承: extends关键字

ES6增加了extends关键字来创建一个类的子类。

例子:

本例子,在子类的constructor内使用了super关键字

//使用了ES6的句法:
class Animal { constructor(name) {
this.name = name; } speak() { console.log(this.name + ' makes a noise.'); } } //如果一个constructor在子类中存在,它需要调用super()后,才能使用this. class Dog extends Animal { constructor(name) { super(name); // call the super class constructor and pass in the name parameter } speak() { console.log(this.name + ' barks.'); } } let d = new Dog('Mitzie'); d.speak(); // Mitzie barks.
// 如果Dog不使用constructor函数,也可以。但使用的化,必须配合super()。

 

另外,可以使用传统的function-based "classes"

function Animal (name) {
  this.name = name;  
}

Animal.prototype.speak = function () {
  console.log(this.name + ' makes a noise.');
}

class Dog extends Animal {
  speak() {
    console.log(this.name + ' barks.');
  }
}

let d = new Dog('Mitzie');
d.speak(); // Mitzie barks.

 

再次强调,JavaScript的类是prototype-based的

Dog.prototype
//Animal {constructor: ƒ, speak: ƒ}

 

 

类classes 不能extend标准对象(non-constructible)。

因此,如果你想要继承自一个regular object。你需要使用方法Object.setPrototypeof()方法

const Animal = {
  speak() {
    console.log(this.name + 'makes a noise')
  }
}

class Dog {
  constructor(name) { this.name = name}
}

Object.setPrototypeOf(Dog.prototype, Animal)

let d = new Dog('wang')
d.speak() 

 

 

使用super关键字可以调用父类的方法

除了上文提到的在子类的constructor中使用super(params...),

还可以在子类中的方法中使用super.

例子:

class Lion extends Cat {
  speak() {
    super.speak();
    console.log(`${this.name} roars.`);
  }
}

 

Modules in ES6

在ES6之前,JS不支持modules。现在有了import和export操作符。

在ES5, 常见使用

你可能感兴趣的:(JavaScript的几个概念简单理解(深入解释见You Don't know JavaScript这本书))