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 declaredvar 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 = 0function 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.printNameprintNameAgain(); // 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 hoistedreturn {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 = $('<div id="myDiv">This is a div.</div>');
// myDiv now represents a jQuery object referencing a DOM node.
const myText = $('<p/>');
// myText now represents a jQuery object referencing an HTMLParagraphElement.
const myInput = $('<input />');
// 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 classesBridge    Separates an object’s interface from its implementationComposite    A tree structure of simple and composite objectsDecorator    Add responsibilities to objects dynamicallyFacade    A single class that represents an entire subsystemFlyweight    A fine-grained instance used for efficient sharingProxy    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]]的值是一个对象.

使用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 AmI do not like green eggs and hamLunchtime 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, 常见使用<script>标签和IIFE(即使用()();),或者AMD之类的库。实现类似功能。

流行的写法:

//module.js
export var port = 3000
export function getCar(model) {...}//main.js
import {port, getCar} from 'module';

可选的,使用as关键字把引进的存入一个变量:

import * as service from 'module'
console.log(service.port) //3000

语法:

//输出变量name1, name2
export { name1, nam2, }
//使用别名
export { var1 as name1 }
//声明+赋值,然后输出。(如果不赋值,则值默认为undefined)
export let name1 = 100
//输出函数, class都可以。
export function Name() {...}//输出默认的表达式, 函数,class
export default expression
export { name1 as default, ...}export * from ...;
export { name1, ...} from...;
export { import as name1, ...} from ...;
export { default} from ...;

和ES5的写法比较:

var s = "hello"export { s }
//等同于,使用babel在线编辑器,可以看转化格式。
exports.s = s//或者module.exports = s

CommonJS规范

这种模块加载机制被称为CommonJS规范。在这个规范下,每个.js文件都是一个模块,它们内部各自使用的变量名和函数名都互不冲突,例如,hello.jsmain.js都申明了全局变量var s = 'xxx',但互不影响。

一个模块想要对外暴露变量(函数也是变量),可以用

module.exports = variable;

一个模块要引用其他模块暴露的变量,用

var ref = require('module_name');

深入了解模块原理

JS语言本书并没有一种机制保证不同的模块可以使用相同的变量名。相同的变量名就会冲突!

但,实现模块的奥妙在于: Closures!

把一段js代码用一个函数包裹起来,这段代码的"全局"变量,就成了函数内的局部变量了!

首先了解一下, node.js的模块加载原理:如何让变量名不冲突。

//hello.js
var s = 'Hello';
var name = 'world';console.log(s + ' ' + name + '!');//Node.js加载hello.js, 会使用IIFE
(function () {// 读取的hello.js代码:var s = 'Hello';var name = 'world';console.log(s + ' ' + name + '!');// hello.js代码结束
})();

这样,Node.js加载其他模块时,变量s都是局部的了。

再了解module.exports的实现:

Node先准备一个对象module,

// 准备module对象:
var module = {id: 'hello',exports: {}
};

然后:

module.exports = variables   //这是我们要输出的变量var load = function(module) {return module.exports;
}
var exported = load(module)  //得到要输出的内容。//Node把module变量保存到某个地方
save(module, exported)

由于Node保存了所有导入的module,当我们使用require()获取module时,Node找到对应的module,把这个module的exports变量返回。 这样另一个模块就顺利拿到了模块的输出:

var greet = require('./hello');

ES6之后,直接使用关键字export和import即可!

getter

get syntax绑定一个对象的属性到一个函数,当属性被调用(查询)时,执行这个函数。

//Syntax
{get prop() { ... } }
{get [expression]() { ... } }

介绍:

有时候,让一个属性能够返回动态的可计算的值,是非常方便的,

或者,你可能想要反应这个内部变量的status,却无需使用明确的方法来调用。

在JavaScript,可以通过使用getter来完成。

⚠️,一个属性,不能同时既绑定一个函数,又有一个明确的值。

//以下都是❌的//属性x,不能有多个函数绑定。{ get x() { }, get x() { } } //属性x, 不能同时既有明确的值,又有函数生成的计算后返回的值。
{ x: ..., get x() { } } 

⚠️,使用getter的属性,只能有0个或1个参数。

⚠️, delete 操作符,可以删除一个getter。

例子:

在对象初始化时,定义一个getter。

var obj = {log: [1, 2, 3],get latest() {if (this.log.length == 0) {return undefined;}        return this.log[this.log.length - 1];}
}console.log(obj.latest); // "3".

// 使用delete操作符号,删除一个getterdelete obj.latestobj.latest //undefined

getter也可以使用computed property name:

var expr = 'foo';var obj = {get ["You_" + expr]() {return 'hahaha!!!'}
}obj.You_foo
//"hahaha!!!"

Smart/lazy/self-overwriting getters

默认的getters,是懒惰的:

Getters可以让你定义一个对象的属性,但是如果你不存取(使用)它,这个属性的值不会被计算。(类似函数)

setter

set句法绑定一个对象的属性到一个函数,但想要用这个属性改变什么,调用这个函数。

{set prop(val) {...}}
{set [expression](val) {} }

当想要改变一个指定的属性时,调用这个函数。

setter和getter一起使用,创建一个伪属性的类型。a type of pseudo-property。

⚠️

必须有一个明确的参数。

禁止:( { set x(v) { }, set x(v) { } } and { x: ..., set x(v) { } }

setter可以被delete方法删除。

例子

在对象初始化时定义一个setter。

var language = {set current(name) {this.log.push(name)},log: []
}language.current = 'En'
//"En"
language.current = 'Fa'
//"Fa"
language.log
//(2) ["En", "Fa"]

⚠️,因为没有使用getter,所以language.current会得到undefined。

如果要给已经存在的对象,添加一个setter:使用

Object.defineProperty()

var o = {a: 0}
Object.defineProperty(o, 'b', {set: function(x) { this.a = x/2}})

o.b = 10
o.a //5

和getter一样,setter也可以使用计算属性名字。

结论

不是所有浏览器都支持ES6的所有功能。因此需要使用编译器如Babel.

Babel可以是Webpack的一个插件。也可以单独作为tool使用。

关于为什么要使用编译器:见这篇文章:

https://scotch.io/tutorials/javascript-transpilers-what-they-are-why-we-need-them

转载于:https://www.cnblogs.com/chentianwei/p/10197813.html

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

  1. 性能测试之性能测试、负载测试、压力测试、稳定性测试概念简单理解和区分

    性能测试概念描述: 以系统设计初期规划的性能指标为预期目标,对系统不断施加压力,验证系统在资源可接受范围内,是否能达到性能瓶颈. 关键词提取理解:有性能指标,验证 性能测试目标: 1.验证系统的性能指 ...

  2. 对面象对象概念的理解、解释

    写在前面的话: 由于工作原因已经半年多没好好理下Java了,现在因换工作需要重新复习一下Java.以前一直想把自己在Java方面的学习心得写出来,现在终于算是有些时间了,那么开始吧. 这一篇讲对象(O ...

  3. 浏览器解析jsx_简单理解JavaScript,TypeScript和JSX

    原标题:简单理解JavaScript,TypeScript和JSX Java: 基本概念: Java一种直译式脚本语言,是一种动态类型.弱类型.基于原型的语言,内置支持类型.它的解释器被称为Java引 ...

  4. Javascript闭包简单理解

    Javascript闭包简单理解 原文:Javascript闭包简单理解 提到闭包,想必大家都早有耳闻,下面说下我的简单理解. 说实话平时工作中实际手动写闭包的场景并不多,但是项目中用到的第三方框架和 ...

  5. 简单理解VO、DTO、PO、DO的概念、区别

    简单理解VO.DTO.PO.DO的概念.区别 概念 VO(View Object) 视图对象,用于表现层,对指定页面或者组件需要的数据进行封装. DTO(Date Transfer Object) 数 ...

  6. 十分钟,快速理解JavaScript中的闭包概念

    海阔凭鱼跃,天高任鸟飞.Hey 你好!我是猫力Molly 闭包已经是一个老生常谈的问题了,不同的人对闭包有不同的理解.今天我来浅谈一下闭包,大家一起来就"闭包"这个话题,展开讨论, ...

  7. javascript引擎执行的过程的理解--执行阶段

    一.概述 js引擎执行过程主要分为三个阶段,分别是语法分析,预编译和执行阶段,上篇文章我们介绍了语法分析和预编译阶段,那么我们先做个简单概括,如下: 1.语法分析: 分别对加载完成的代码块进行语法检验 ...

  8. JavaScript面向对象--继承 (超简单易懂,小白专属)...

    JavaScript面向对象--继承 (超简单易懂,小白专属) 一.继承的概念 子类共享父类的数据和方法的行为,就叫继承. 二.E55如何实现继承?探索JavaScript继承的本质 2.1构造函数之 ...

  9. javascript 近乎神话般的概念:闭包

    写在前面 JavaScript 一个近乎神话 对于JavaScript有使用经验但却从未真正理解闭包概念的人来说,理解闭包可以说是某种意义上的重生.闭包并不是需要学习新的语法才能使用的工具.闭包的产生 ...

最新文章

  1. UVA1626 括号序列 Brackets sequence(区间DP匹配括号,输出匹配方案)
  2. 12层也能媲美ResNet?YOLOv4一作邓嘉团队提出ParNet:非深度网络!
  3. 梅捷SY-A780G+冷启动黑屏,需Reset才能进入系统
  4. an example of Ascii using 问号 as example
  5. 【转】GPS误差来源
  6. 互联网账户系统如何设计
  7. SpringBoot多数据源(主从数据源)配置
  8. 408计算机考研大纲 doc,2020计算机专业408基础综合考研大纲
  9. LeetCode 680. 验证回文字符串 Ⅱ
  10. MySQL基础思维导图
  11. BUG解决:RuntimeError:Given groups=1,weight of size...expected input...but got 3 channels instead.
  12. php $表达式,PHP表达式概念及实例详解
  13. python基础入门第0天
  14. 重新开始我的blog内容!
  15. Linux上,最常用的一批命令解析
  16. PCA主成分分析/LDA线性判别分析/CCA典型相关分析 对比
  17. 设计一个AOA蓝牙精准室内定位系统
  18. CVPR引起巨大争议的新技术Face2Face:当科技先进到让人害怕
  19. 基于python的饭店点餐外卖管理系统#毕业设计
  20. retina屏下的1px线的实现

热门文章

  1. IOS第四天-新浪微博 -存储优化OAuth授权账号信息,下拉刷新,字典转模型
  2. 抽象工厂模式-与-工厂方法模式区别
  3. es_分组-分页-TransportClient实现
  4. mysql对执行结果进行html格式的输出?输出html格式?
  5. AI领域内,敢和BAT决斗的创业公司都在这了!
  6. 求一颗二叉树中两个节点的最低公共父节点
  7. bug4 导入新工程时报 Target runtime com.genuitec.runtime.generic.jee60 is not defined
  8. 【转】 [C/OC的那点事儿]NSMutableArray排序的三种实现(依赖学生成绩管理系统).
  9. 性能测试知多少--系统计数器与硬件分析
  10. Catalyst 2950/2955交换机的RSPAN配置