Coder Social home page Coder Social logo

es6's People

Contributors

songstar0904 avatar

Watchers

 avatar

es6's Issues

第7节:ES6中新增的数组知识(1)

Array.from()

标准的JSON数组格式,跟普通的JSON对比是在最后多了一个length属性。(类数组格式是普通的数组后面多了一个length属性)。Array.from() 方法从一个类似数组或可迭代对象中创建一个新的数组实例。
不仅是json数组, 类数组也能通过Array.from()转换成数组。在ES5的时候我们是使用Array.prototype.slice.call()

let json = {
  '0': 1,
  '1': 2,
  length: 2
}
console.log(Array.from(json)); // [1, 2]
console.log(Array.prototype.slice.call(json)); // [1, 2]
  • Array from a String
console.log(Array.form('foo'); // ["f", "o", "o"]
  • Array from a Set(数组去重)
console.log(Array.from(new Set([1, 2, 3, 1, 3]))); // [1, 2, 3]
  • Array from a Map
let m = new Map([[1, 2], [2, 4], [4, 8]]);
Array.from(m); // [[1, 2], [2, 4], [4, 8]]

Array.of()

Array.of() 方法创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型。
Array.of() Array 构造函数之间的区别在于处理整数参数:Array.of(7) 创建一个具有单个元素 7 的数组,而 Array(7) 创建一个包含 7 个 undefined 元素的数组。

console.log(Array.of(7)); // [7]
console.log(Array.of(1, 2, 3)); // [1, 2, 3]

console.log(Array(7)); //  [empty × 7]
console.log(Array(1, 2, 3)); // [1, 2, 3]

Array.prototype.find()

** find() 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。**

  • 寻找数组中的质数
function isPrime(item, index, arr) {
	var start = 2;
	while (start <= Math.sqrt(item)) {
		if (item % start ++ < 1) {
			return false;
		}
	}
	return item > 1;
}
console.log([4, 6, 8, 12].find(isPrime)); // undefined
console.log([4, 5, 8, 12].find(isPrime)); // 5
  • findIndex() 方法,它返回数组中找到的元素的索引,而不是其值。

如果你需要找到一个元素的位置或者一个元素是否存在于数组中,使用Array.prototype.indexOf() 或 Array.prototype.includes()。

第6节:ES6数字操作

二进制和八进制

  • 二进制声明
    二进制的英文单词是Binary,二进制的开始是0(零),然后第二个位置是b(注意这里大小写都可以实现),然后跟上二进制的值就可以了。
let binary = 0B010101;
console.log(binary); // 21
  • 八进制声明
    八进制的英文单词是Octal,也是以0(零)开始的,然后第二个位置是O(欧),然后跟上八进制的值就可以了。
let b = 0o666;
console.log(b); // 438

数字判断和转换

  • 数字验证Number.isFinite( xx )
    可以使用Number.isFinite( )来进行数字验证,只要是数字,不论是浮点型还是整形都会返回true,其他时候会返回false。
console.log(Number.isFinite(11 / 4)); // true
console.log(Number.isFinite('a'));//false
console.log(Number.isFinite(NaN));//false
console.log(Number.isFinite(undefined));//false
  • NaN验证
    NaN是特殊的非数字,可以使用Number.isNaN()来进行验证。下边的代码控制台返回了true。
console.log(Number.isNaN(NaN)); // true
  • 判断是否为整数Number.isInteger(xx)
console.log(Number.isInteger(1.2)); //false
  • 整数转换Number.parseInt(xxx)和浮点型转换Number.parseFloat(xxx)
console.log(Number.parseInt(1.2)); // 1
console.log(Number.parseFloat(1.2)); // 1.2
  • 最大安全整数and最小安全整数
console.log(Number.MAX_SAFE_INTEGER);
console.log(Number.MIN_SAFE_INTEGER);
  • 安全整数判断isSafeInteger( )
let a= Math.pow(2,53)-1;
console.log(Number.isSafeInteger(a));// false

第16节:promise对象的使用(待补)

Promise 对象用于一个异步操作的最终完成(或失败)及其结果值的表示。(简单点说就是处理异步请求。我们经常会做些承诺,如果我赢了你就嫁给我,如果输了我就嫁给你之类的诺言。这就是promise的中文含义:诺言,一个成功,一个失败。)

创建Promise对象

Promise 对象是由关键字 new 及其构造函数来创建的。该构造函数会?把一个叫做“处理器函数”(executor function)的函数作为它的参数。这个“处理器函数”接受两个函数——resolvereject ——作为其参数。当异步任务顺利完成且返回结果值时,会调用 resolve 函数;而当异步任务失败且返回失败原因(通常是一个错误对象)时,会调用reject 函数。

{
	let myPromise = new Promise((resolve, reject) => {
		// 当异步代码执行成功是, 调用resolve(),失败调用reject()
		// 我们使用setTimeout()来模拟异步代码
		setTimeout(() => {
			resolve('resolve成功');
		}, 250);
	});
	myPromise.then((res) => {
		console.log('收到信息:' + res); // 收到信息:resolve成功
	})
}

使用 XHR 加载图像

使用 XHR 加载图像

第8节:ES6中新增的数组知识(2)

Array.prototype.fill()

fill() 方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。

参数arr.fill(value, start, end)

  • value: 用来填充数组元素的值。
  • start: (可选)起始索引,默认值为0。
  • end: (可选)终止索引,默认值为 this.length。
let arr = [1, 2, 3, 4];
console.log(arr.fill('f', 1, 3)); // [1, "f", "f", 4]

数组遍历

for...of

for...of语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句。

let arr_of = ['foo', 'bar'];
for (let item of arr_of) {
	console.log(item); //foo bar
}
for (let index of arr_of.keys()) {
	console.log(index); //0 1
}
for (let [index, item] of arr_of.entries()) {
	console.log(index + ': ' + item); // 0: foo 1: bar
}
  • for...of与for...in的区别
    无论是for...in还是for...of语句都是迭代一些东西。它们之间的主要区别在于它们的迭代方式。
    for...in 语句以原始插入顺序迭代对象的可枚举属性。
    for...of语句遍历可迭代对象定义要迭代的数据。
Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};
let iterable = [3, 5, 7];
iterable.foo = 'hello';
for (let i in iterable) {
	console.log(i); // 0 1 2 foo arrCustom objCustom
}
for (let i in iterable) {
	if (iterable.hasOwnProperty(i)) {
		console.log(i); // 0 1 2 foo
	}
}
for (let i of iterable) {
	console.log(i); // 3 5 7
}

Object.entries()

Object.entries()方法返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环也枚举原型链中的属性)。

// array like object with random key ordering
const anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.entries(anObj)); // [ ['2', 'b'], ['7', 'c'], ['100', 'a'] ]

// getFoo is property which isn't enumerable
const myObj = Object.create({}, { getFoo: { value() { return this.foo; } } });
myObj.foo = 'bar';
console.log(Object.entries(myObj)); // [ ['foo', 'bar'] ]
  • 将Object转换为Map
    new Map() 构造函数接受一个可迭代的entries。借助Object.entries方法你可以很容易的将Object转换为Map:
var obj = {foo: 'bar', baz: 42};
console.log(new Map(Object.entries(obj))); // Map(2) {"foo" => "bar", "baz" => 42}

第17节:class类的使用

简介

JavaScript 语言中, 生成实例对象的传统方法是通过构造函数。

function Point(x, y) {
  this.x = x;
  this.y = y;
}
Point.prototype.add = function() {
  return this.x + this.y;
}
var p = new Point(1, 2); 
p.add(); // 3

ES6 提供了更接近传统语言的写法, 引入了Class 类这个概念, 作为对象的模板。 通过class 关键字, 可以定义类。

{
	class Point {
		constructor(x, y) {
			this.x = x;
			this.y = y;
		}
		add () {
			return this.x + this.y
		}
	}
	var p = new Point(1, 2);
	p.add(); // 3	
}

上面定义了一个类, 可以看到里面有一个constructor方法, 这就是构造方法, 而this关键字则代表实例对象。
Point类除了构造方法, 还定义了add方法, 注意:

  • 类的方法前面不需要加function这个关键字,
  • 另外方法之间不需要,隔开,否则会报错。
  • 使用的时候必须要加new, 不加会报错(构造函数可省略)
    ES6的类, 完全可以看作构造函数的另一种写法。
typeof Point // function
Point === Point.prototype.constructor // true

类的所有方法都定义在类的'prototype'属性上。所有属性都定义在它的实例(定义在this变量上)上。

class Point{
  constructor () { ... }
  add () { ... }
}
// 等同于
Point.prototype = {
  function constructor () { ... },
  function add () { ... }
}

在类的实例上面调用方法, 其实是调用原型上的方法。
prototype对象的constructor属性, 直接指向类的本身, 这与ES5 的行为一致。

{
	class B {};
	let b = new B();
	b.constructor === B.prototype.constructor; // true
	B.prototype.constructor === B; // true
}

由于类的方法定义在prototype对象上面, 所以类的新方法可以添加在prototype对象上。Object.assign 方法可以很方便的添加多个方法。

{
	class Point {
		constructor () { ... }
	}
	Object.assign(Point.prototype, {
		add () {},
		toString () {}
	});
}

类的内部所有方法都是不可枚举的。这一点与ES5行为不一致。

{
	class Point {
		constructor () {}
		add () {}
	}
	Object.keys(Point.prototype); // []
	Object.getOwnPropertyNames(Point.prototype); // ["constructor", "add"]

	var Point2 = function () {}
	Point2.prototype.add = function () {}
	Object.keys(Point2.prototype); // ["add"]
	Object.getOwnPropertyNames(Point2.prototype); // ["constructor", "add"]
}

类的属性名, 可以采用表达式。

{
	let name = 'add';
	class Point {
		constructor () {}
		[name] () {}
	}
}

严格模式

类和模块的内部,默认就是严格模式,所以不需要使用use strict指定运行模式。只要你的代码写在类或模块之中,就只有严格模式可用。

constructor 方法

constructor方法是类的默认方法, 通过new命令生成对象实例时, 自动调用该方法。一个类必须要有constructor方法, 如果没有显示定义, 则会被默认添加一个空的constructor方法。
constructor方法默认返回实例对象(this), 完全可以制定返回其他对象。

class Foo {
  constructor () {
    return Object.create(null);
  }
}
new Foo() instanceof Foo // false

类的实例对象

与ES5一致, 共享一个原型对象。

{
	class Point {};
	let p1 = new Point();
	let p2 = new Point();
	p1.prototype === p2.prototype; // true
	p1.__proto__ === p2.__proto__; // true
}

class 表达式

与函数一样, 类也可以使用表达式是形式定义。

{
	let MyClass = class Me {
		getClassName () {
			return Me.name;
		}
	};
	let inst = new MyClass();
	inst.getClassName(); // Me
	MyClass.name; // Me
	Me.name; // Me is not defined
}

上面代码使用表达式定义类。 这个类的名字是MyClass而不是MeMe 只在Class内部使用, 指代当前类。
如果类的内部没有用到的话, 可以省略Me

let MyClass = class { ... }

可以像函数一样立即执行。

{
	let p = new class {
		constructor(name) {
			this.name = name;
		}
		sayName () {
			console.log(this.name);
		}
	}('songStar');
	p.sayName(); // songStar
}

不存在变量提升

类不存在变量提升(hoist),这一点与 ES5 完全不同。

new Foo(); // 报错
class Foo {}

私有方法和私有属性

私有方法是常见需求, 但是ES6不提供, 只能通过方法模拟实现。

  • 在命名上加以区别。
{
	class Foo {
		// 共有方法
		baz () {
			this._bar();
		}
		// 私有方法
		_bar () {
			return '_bar';
		}
	}
}

上面代码中, _bar方法前面的下划线, 表示这是一个只限于内部使用的私有方法。 但是, 这种命名不是保险的, 在类的外部, 还是可以调用这个方法。

  • 另一种方法索性将私有方法洗出模块, 因为模块内部的所有方法都是对外可见的。
{
	class Foo {
		baz () {
			bar.call(this);
		}
	}
	function bar() {
		return 'bar';
	}
}
  • 利用Symbol值的唯一性, 将私有方法的命名为一个Symbol值。
{
	let bar Symbol('bar');
	let snaf = Symbol('snaf');
	export default class MyClass {
		// 公有方法
		foo (baz) {
			this[bar](baz);
		}
		// 私有方法
		[bar](baz) {
			return this[snaf] = baz;
		}
	}
}

上面代码中, bazsnaf都是Symbol值, 导致第三方无法获取到它们, 因此达到了私有方法和私有属性的效果。

私有属性提案

与私有方法一样, ES6不支持私有属性, 目前有一个提案, 为class加了私有属性。 在属性名前加#

{
	class Point{
		#x;
		constructor (x = 0) {
			#x = +x; // === this.#x = +x;
		}
		get x () {
			return #x;
		}
		set x (value) {
			#x = +value;
		}
		#add (value) {
			#x += value;
		}
	}
}

上面代码中, #x表示私有属性x, 在类之外是读取不到这个属性的。 还可以看到私有属性与实例的属性可以同名的(#xget x())。#sum()就是一个私有方法。
之所以要引入一个新的前缀#表示私有属性,而没有采用private关键字,是因为 JavaScript 是一门动态语言,使用独立的符号似乎是唯一的可靠方法,能够准确地区分一种属性是否为私有属性。
另外,私有属性也可以设置 gettersetter 方法。

this 指向

类的方法内部如果含有this, 它默认指向类的实例。
一旦单独使用该方法, 很可能报错。

{
	class Point{
		sayName (name) {
			this.print(`hello${name}`);
		}
		print (text) {
			console.log(text);
		}
	}
	let p = new Point();
	let {sayName} = p;
	sayName('songStar'); // TypeError: Cannot read property 'print' of undefined
}

上面代码中, sayName方法中this, 默认指向Point类的实例。 但是如果将这个方法图取出来单独使用,this会指向该方法的运行所在运行环境, 因此找不到print方法。

  • 通过在构造方法中绑定this
{
	class Point {
		constructor () {
			this.sayName = this.sayName.bind(this)
		}
	}
}
  • 使用箭头函数解决。
{
	class Point {
		constructor () {
			this.sayName = (name) => {
				this.print(`hello ${name}`);
			}
		}
	}
}
  • 使用Proxy, 获取方法的时候, 自动绑定this
{
	function selfish (target) {
		const cache = new WeakMap();
		const handler = {
			get (target, key) {
				const value = Reflect.get(target, key);
				if (typeof value !== 'function') {
					return value;
				}
				if (! cache.has(value)) {
					cache.set(value, value.bind(target));
				}
				return cache.get(value);
			}
		};
		const proxy = new Proxy(target, handler);
		return proxy;
	}
	let p = new selfish(new Point());
}

第1节:ES6的开发环境搭建

建立工程目录

先建立一个项目的工程目录, 并在目录下便建立两个文件夹: srcdist

  • src: 书写ES6代码的文件夹, 写的js程序都放在里面。
  • dist: 利用Babel编译成ES5代码的文件夹, 在HTML页面需要引入的时这里的js文件。

编写index.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>build es6</title>
	<script src="./dist/index.js"></script>
</head>
<body>
	Hello es6
</body>
</html>

需要注意的是引入js文件时,引入的是dist目录下的文件。

<script src="./dist/index.js"></script>

编写index.js

在src目录下, 新建index.js 文件。

let a = 2;
const b = 5;
console.log(a, b);

初始化项目

在安装Babel之前, 需要npm init 先初始化项目。 打开命令行工具, 进入项目目录, 输入命令。

npm init -y

-y 代表全部默认同意, 就不要一次次按回车了。 命令执行完成后会在项目根目录下产生package.json文件。

{
  "name": "es6",
  "version": "1.0.0",
  "description": "learn es6 by jspang",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/songStarr/es6.git"
  },
  "keywords": [],
  "author": "songStar",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/songStarr/es6/issues"
  },
  "homepage": "https://github.com/songStarr/es6#readme"
}

可以根据自己的需要进行修改。

全局安装Babel-cli

在终端中输入以下命令,如果你安装很慢的话,可以使用淘宝镜像的cnpm来进行安装。

cnpm install -g babel-cli

虽然已经安装了babel-cli, 只是这样还不能成功进行转换。

本地安装babel-preset-es2015 和 babel-cli

cnpm install --save-dev babel-preset-es2015 babel-cli

安装完成后, 我们可以看一下我们的package.json文件, 已经多了devDependencies选项。

"devDependencies": {
    "babel-cli": "^6.26.0",
    "babel-preset-es2015": "^6.24.1"
  }

新建.babelrc

在根目录下新建.babelrc文件, 并打开加入下面代码。

{
  "presets": ["es2015"],
  "plugins": []
}

这个文件我们建立完成后, 现在可以在终端输入的转换命令了。

babel src/index.js -o dist/index.js

可以发现dist目录下自动添加了一个index,js的文件,且内容正是我们想要的ES5。

"use strict";

var a = 2;
var b = 5;
console.log(a, b);

简单转化命令

在学习vue 的时候,可以使用npm run build 直接利用webpack进行打包,在这里也希望利用这种方式完成转换。打开package.json文件,把文件修改成下面的样子。

"scripts": {
    "build": "babel src/index.js -o dist/index.js"
  }

这样我们就能使用npm run build 来进行转换了。

第18节:模块化操作

name 属性

由于本质上,ES6 的类只是 ES5 的构造函数的一层包装,所以函数的许多特性都被Class继承,包括name属性。

class Point {}
Point.name // Point

name属性总是返回紧跟在class关键字后面的类名。

Class 的取值函数(getter) 和存值函数(setter)

第4节:扩展运算符和rest运算符

对象扩展运算符(...)

当编写一个方法时, 我们允许它传入的参数是不确定的, 就可以使用对象扩展运算符作参数。

function foo(...arg){
  console.log(arg, arg[3]); // [1, 2, 3]  undefined
}
foo(1, 2, 3);

undefined说明是可以传入多个值,并且就算方法中引用多了也不会报错。

扩展运算符的用处

  • 赋值(深拷贝)
    浅拷贝只是拷贝基本类型的数据,如果父对象的属性等于数组或另一个对象,那么实际上,子对象获得的只是一个内存地址,因此存在父对象被篡改的可能(浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存)。深拷贝就是能够实现真正意义上的数组和对象的拷贝。它的实现并不难,只要递归调用"浅拷贝"就行了。(深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象)
// 浅拷贝
let arr1 = [1, 2];
let arr2 = arr1;
arr2.push(3);
console.log(arr1, arr2); // [1, 2, 3] [1, 2, 3]
// es5
let arr1 = [1, 2];
let arr2 = arr1.slice(0);
arr2.push(3);
console.log(arr1, arr2); // [1, 2] [1, 2, 3]
// es6
let arr1 = [1, 2];
let arr2 = [...arr1]
arr2.push(3);
console.log(arr1, arr2); // [1, 2] [1, 2, 3]

rest运算符

如果你已经很好的掌握了对象扩展运算符,那么理解rest运算符并不困难,它们有很多相似之处,甚至很多时候你不用特意去区分。它也用…(三个点)来表示,我们先来看一个例子。

function foo(first, ...arg){
  console.log(arg);
}
foo(0, 1, 2, 3); // [1, 2, 3]
  • 循环输出rest运算符
function foo(first, ...arg){
  for(let val of arg) {
    console.log(val);
  }
}
foo(0, 1, 2, 3); // 1 2 3

for...of循环可以避免我们开拓内存空间, 增加代码运行效率。

第3节:变量的解构赋值

定义:ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。解构赋值在实际开发中可以大量减少我们的代码量,并且让我们的程序结构更清晰。

数组解构赋值

// es5
let a = 0;
let b = 1;
// es6
let [a, b] = [0, 1];
  • 数组模式和复制模式统一
let [a, [b, c], d] = [1, [2, 3], 4];
  • 解构默认值
let [foo = true] = [];
console.log(foo); // true
// 需要注意的是undefined和null的区别。
let [a, b = 'JSPang'] = ['技术胖', undefined];
console.log(a + b); // 技术胖JSPang
let [a, b = 'JSPang'] = ['技术胖', null];
console.log(a + b); // 技术胖null

对象解构赋值

注意:对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。

let {foo, bar} = {foo: 'JSPang', bar: '技术胖'};
console.log(foo + bar); // JSPang技术胖

圆括号的使用

如果在解构之前就定义了变量,这时候你再解构会出现问题。下面是错误的代码,编译会报错。

let foo;
{foo} = {fpp: 'JSPang'};
console.log(foo); // SyntaxError

要解决报错,使程序正常,我们这时候只要在解构的语句外边加一个圆括号就可以了。

let foo;
({foo} = {foo: 'JSPang'});
console.log(foo); // JSPang

字符串结构

const [a, b, c] = 'abc';
console.log(a, b, c); // a b c

第15节:用Proxy进行预处理

Proxy 对象用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)。

语法

let p = new Proxy(target, handler);

  • target 用Proxy包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
  • handler 一个对象,其属性是当执行一个操作时定义代理的行为的函数。

基础示例

{
	let target = {
		add: function (val) {
			return ++val;
		},
		name: 'JSPang'
	};
	let handler = {
		get: function (target, key, property) {
			return target[key];
		},
		set: function (target, key, val, receiver) {
			console.log(`setting ${key}: ${target[key]} ===> ${key}: ${val}`);
			return target[key] = val;
		}
	}
	var p = new Proxy(target, handler);
	console.log(p.add(1));
	p.name = 'Songstar'; // setting name: JSPang ===> name: Songstar
}

apply的使用

{
	let target = () => 'a string';
	let handler = {
		apply (target, ctx, args) {
			console.log('do apply');
			return Reflect.apply(...arguments);
		}
	}
	var p = new Proxy(target, handler);
	console.log(p()); // a string
}

功能

  • 拦截和见识外部对对象的访问
  • 降低函数或类的复杂度
  • 在复杂操作前对操作进行校验或对所需资源进行管理

使用场景

实例解析ES6 Proxy使用场景

第13节:Set和WeakSet数据结构

Set对象允许你存储任何类型的唯一值, 无论是原始值或者是对象引用。

Set实例

所有Set实例继承自Set.prototype。

属性

  • Set.prototype.constructor
    返回实例的构造函数。 默认情况下是Set。
  • Set.prototype.size
    返回Set对象的值的个数。

方法

  • Set.prototype.add(value)
    在Set对象尾部添加一个元素, 返回Set对象。
  • Set.prototype.clear()
    移除Set对象内所有元素。
  • Set.prototype.delete(value)
    移除Set中与这个值相等的元素, 返回Set.prototype.has(value)在这个操作前会返回的值(即如果该元素存在, 返回true, 否则返回false)。
  • Set.prototype.entries()
    返回一个新的迭代器对象, 该对象包含Set对象中按插入顺序排序的所有元素值[value, value]数组。 为了使这个方法和Map对象保持相似, 每个值的键和值相等。
  • Set.prototype.forEach(callbackFn[, thisArg])
    按插入顺序, 为Set对象中每个值调用一次callbackFn。 如果提供了thisArg参数, 回调中的this为这个参数。
  • Set.prototype.has(value)
    返回一个布尔值, 表示该值在Set中存在与否。
  • Set.prototype.keys()
    values()方法相同,返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值。
  • Set.prototype.values()
    返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值。
  • Set.prototype[@@iterator]()
    返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值。

使用Set对象

{
	let mySet = new Set([0]);
	mySet.add(1); // Set(2) {0, 1}
	// Set对象已存在, 则无效 (数组去重)
	mySet.add(1); // Set(2) {0, 1}
	mySet.add("some text"); // Set(3) {0, 1, "some text"}
	mySet.add({a: 1, b: 2}); // Set(4) {0, 1, "some text", {…}}
	mySet.add({a: 1, b: 2}); // Set(5) {0, 1, "some text", {…}, {…}} 对象指向的是不同的对象,所以没问题
	mySet.has(1); //true
	mySet.size; // 5
	mySet.delete(1); // true, 移除1
	mySet.has(1); // false
	mySet.clear(); // Set(0) {} 移除所有 
}
  • 迭代Set
{
	let mySet = new Set([1, 'some text']);
	for (let item of mySet) {
		console.log(item); // 1 some text
	}
	//(键与值相等)
	for (let item of mySet.keys()) {
		console.log(item); // 1 some text
	}
	for (let [key, value] of mySet.entries()) {
		console.log(key); // 1 some text
	}
	for (let item of mySet.values()) {
		console.log(item); // 1 some text
	}
}
  • Array 相关
// 转换Set为Array
var mtArr = [v for (v of mySet)]; // [1, "some text"]
// Set 和 Array互换
mySet2 = new Set([1,2,3,4]);
mySet2.size; // 4
[...mySet2]; // [1,2,3,4]

WeakSet

WeakSet 对象允许你将弱保持对象存储在一个集合中。

{
	let weakObj = new WeakSet();
	let obj = {a: 1, b: 2};
	weakObj.add(obj); // WeakSet {{…}}
}

这里需要注意的是,如果你直接在new 的时候就放入值,将报错。
WeakSet里边的值也是不允许重复的

第14节:map数据结构

Map 对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。

Object 和 Map 比较

ObjectMap类似的一点就是它们都允许你按键存取一个值, 都可以删除键, 还可以检测一个键是否绑定值。区别在于:

  • 一个对象通常都有自己的原型, 所以一个对象总有一个prototype键。 不过ES5 开始使用obj = Object.create(null)来创建一个没有原型的对象。
  • 一个对象的键只能是字符串或者Symbols, 但是Map键可以是任意键。
  • 你可以通过size属性很容易的得到一个Map键值对的个数, 而对象的键值对个数只能手动确认。

使用Map

{
	var myMap = new Map();
	var keyObj = {},
	    keyFunc = function () {},
	    keyString = 'a string';
	// 添加键
	myMap.set(keyString, 'Sting 键的值');
	myMap.set(keyObj, 'Obj 键的值');
	myMap.set(keyFunc, 'Func 键的值');
	console.log(myMap); // Map(3) {"a string" => "Sting 键的值", {…} => "Obj 键的值", ƒ => "Func 键的值"}
	// 读取值
	myMap.get(keyFunc); // "Func 键的值"
	myMap.size; // 3
	myMap.delete(keyString); // true
	myMap.size; // 2
	myMap.clear();
	console.log(myMap); // Map(0) {}
}
  • 将NaN作为映射的键
    NaN 也可以作为Map对象的键, 虽然NaN 和任何值甚至和自己都不想等(NaN !== NaN), 但是两个NaN作为Map的键来说没有区别。
{
	let myMap = new Map();
	myMap.set(NaN, 'not a Number');
	myMap.get(NaN); // 'not a Number'
	let otherNaN = Number('foo');
	myMap.get(otherNaN); // 'not a Number'
}
  • 迭代映射
  1. for..of循环来实现迭代
  2. forEach()方法迭代
  • 映射与数组对象的关系
{
	let arr = [['key1', 'val1'], ['key2', 'val2']];
	let myMap = new Map(arr);
	myMap.get('key1'); // val1
	console.log(JSON.stringify([...myMap])); // [["key1","val1"],["key2","val2"]]
}

ES6中的箭头函数和扩展

箭头函数表达式的语法比函数表达式更短, 并且不绑定自己的this, arguments, super 或 new.target。 这些函数表达式最适合用于非方法函数, 并且他们不能用作构造函数。

  • 更短函数
let materials = ['foo', 'bar', 'baz'];
materials.map(function(material) {
	return material.length;
}); // [3, 3, 3]
materials.map(material => material.length); // [3, 3, 3]
  • 不绑定this
  • 与严格模式的关系
    鉴于this是此法层面上的, 严格模式中与this相关的关泽都被忽略。
var f = () => {'user strict'; return this};
f() === window; // true
var b = function() {
	'use strict';
	return this;
}
b(); // undefined
  • 通过call / apply 调用
    由于this已经在此法层面完成了绑定, 通过call() / apply() 方法调用一个函数时, 只是传入的参数而已, this并没有影响
var adder = {
	base: 1,
	add: function(a) {
		var f = v => v + this.base;
		return f(a);
	},
	addThruCall: function(a) {
		var f = v => v + this.base;
		var b = {
			base: 2
		};
		return f.call(b, a);
	}
};
console.log(adder.add(1)); // 2
console.log(adder.addThruCall(1)); // 2 不是3
  • 不绑定arguments
    箭头函数不绑定Arguments对象。 因此, 参数只是在封闭范围内引用相同的名称。
var arguments = 42;
var arr = () => arguments;
arr(); // 42
function foo() {
	var f = i => arguments[0] + i;
	return f(2);
}
foo(1); // 3(1+2)
function foo() {
	var f = function(i) {
		return arguments[0] + i;
    }
    return f(2);
}
foo(1); // 4(2+2)

在大多数情况下, 使用剩余参数是使用arguments对象的好选择。

function foo() { 
  var f = (...args) => args[0]; 
  return f(2); 
}
foo(1); // 2
  • 像方法一样使用箭头函数
    如上所述,箭头函数表达式对非方法函数是最合适的。让我们看看当我们试着把它们作为方法时发生了什么。
var obj = {
	i: 10,
	b: () => console.log(this.i, this),
	c: function() {
		console.log(this.i, this)
	}
}
obj.b(); // undefined windows (严格模式下 undefined)
obj.c(); // 10, Object {...}

箭头函数没有定义this绑定。另一个涉及Object.defineProperty()的示例:

var obj = {
	a: 10
};
Object.defineProperty(obj, 'b', {
	get: () => {
		console.log(this.a, this);
		return this.a + 10;
		// undefined windows
	}
});
  • 使用new操作符
    箭头函数不能用作构造器, 和new 一起使用会抛出错误
var Foo = () => {};
var foo = new Foo(); // TypeError: Foo is not a constructor
  • 使用prototype属性
    箭头函数没有prototype属性。
var Foo = () => {};
console.log(Foo.prototype); // undefined
  • 使用 yield 关键字
    yield 关键字通常不能在箭头函数中使用(除非是嵌套在允许使用的函数内)。因此,箭头函数不能用作生成器。

第2节:新的声明方式

ES6 三中声明方式:

  • var: 他是variable的简写, 可以理解成变量的意思。
  • let : 它在英文中是‘让’的意思, 也可以理解为一中声明的意思。
  • const: 它在英文是常量的意思,常量可以理解为不变的量。

var

用的最多的变量声明方式, 但同时也有一定的问题。 我们都知道在ES6之前,JavaScript没有真正意义上的块级作用域,正是因为只能通过var 来声明变量。这样声明可能出现污染某些变量。比如:

var i = 1;
// ...
for(var i = 0; i < 5; i++){
  // ...
}
console.log(i); // 5

这是一个最常见的栗子🌰, 在全局声明了一个i为1, 经过一个for循环后, i的值产生了变化。

let

  • let 声明的变量用友块级作用域。
  • let 声明的全局遍历不是全局对象的属性, 不可以通过window.变量名的方式访问。
  • 形如for(let x...)的循环每次迭代时都为x创建新的绑定。(某些场景代替闭包)
  • let声明的变量知道控制流到达该变量被定义的代码时才会被装载, 所以它没有声明提前。
{
  let a = 1;
  var A = 1;
}
console.log(A, a); // 1  a is not defined

let b = 2;
var b = 2;
console.log(window.B, window.b); // 2 undefined

console.log(C); // 3
console.log(c); // c is not defined
var C = 3;
let c = 3;

const

定义常量值, 不可以重新复制, 但是如果值是一个对象, 可以改变对象里的属性。

const obj = {'a': 1, 'b': 2};
obj.a = 3;
console.log(obj.a); // 3
obj = {}; // TypeError

第11节:ES6中对象

对象赋值

let name = 'foo';
let fun = 'bar';
var obj = {name, fun};
console.log(obj); // {name: "foo", fun: "bar"}

对象Key值构建

有时候我们会在后台取出key值, 而不是我们前台定义好的, 这时候我们如何构建我们的key值呢?

let key = 'skill';
var obj = {
	[key]: 'web'
};
console.log(obj); // {skill: "web"}

Object.is()

Object.is()方法判断两个值是否是相同的值。
===为同值相等,Object.is()为严格相等。

console.log(+0 === -0); // true
console.log(NaN === NaN); // false
console.log(Object.is(+0, -0)); // false
console.log(Object.is(NaN, NaN)); // true

// Object.assign()
Object.assign()方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象, 它将返回目标对象。

  • 复制一个对象
var obj = {a: 1};
var copy = Object.assign({}, obj);
console.log(copy); // {a: 1}
  • 深拷贝问题
    正对深拷贝, 需要使用其他方法, 因为Object.assign()拷贝的是属性值。 假如源对象的属性值是一个指向对象的引用, 他也只拷贝那个引用值。
var obj1 = {a: 0, b: {c: 0}}; // b是指向对象的引用
var obj2 = Object.assign({}, obj1);
console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}

obj1.a = 1;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}}
console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}

obj2.a = 2;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}}
console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 0}}
  
obj2.b.c = 3;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 3}}
console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 3}}

// Deep Clone
obj1 = { a: 0 , b: { c: 0}};
var obj3 = JSON.parse(JSON.stringify(obj1));
obj1.a = 4;
obj1.b.c = 4;
console.log(JSON.stringify(obj3)); // { a: 0, b: { c: 0}}
  • 合并对象
var o1 = { a: 1 };
var o2 = { b: 2 };
var o3 = { c: 3 };

var obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1);  // { a: 1, b: 2, c: 3 }, 注意目标对象自身也会改变。
  • 继承属性和不可枚举属性是不能拷贝的
var obj = Object.create({foo: 1}, { // foo 是个继承属性。
    bar: {
        value: 2  // bar 是个不可枚举属性。
    },
    baz: {
        value: 3,
        enumerable: true  // baz 是个自身可枚举属性。
    }
});
var copy = Object.assign({}, obj);
console.log(copy); // { baz: 3 }
  • 原始类型会被包装为对象
var v1 = 'abc';
var v2 = true;
var v3 = 10;
var v4 = Symbol('foo');
// 原始类型会被包装,null 和 undefined 会被忽略。
// 注意,只有字符串的包装对象才可能有自身可枚举属性。
console.log(Object.assign({}, v1, null, v2, undefined, v3, v4)); // {0: "a", 1: "b", 2: "c"}

第5节:字符串模版

字符串模板

在ES5的时候, 特别是添加html字符串的时候, 往往很繁杂(有过亲身经历)。如下:

let name = 'songStar';
let sayHtml = '<div class="say">' +
				'大家好,我的名字是<span>' + name + '</span>,很高兴认识大家' +
			  '</div>';
console.log(sayHtml);

+连接符用法很简单,但是一旦字符串多了, 非常麻烦且容易出错, 且每换行都要拼接一次。而ES6对此进行了超级棒的改进。

sayHtml = `<div class="say">
		      大家好,我的名字是<span>${name}</span>,很高兴认识大家
	      </div>`;
console.log(sayHtml);

所有字符串放入单引号里面```, 而变量放在${}中, 且`${}`中可以作运算。

字符串查找

  • indexOf 查找指定字符串, 有则返回索引位置(第一个所查找到的), 无则返回-1。
  • includes 查找指定字符串, 有则返回true, 无则 false。
  • startsWith 判断开头是否存在。
  • endsWith 判断结尾是否存在。
console.log(sayHtml.indexOf('div')); // 1
console.log(sayHtml.includes('div')); // true
console.log(sayHtml.startsWith('<div')); // true
console.log(sayHtml.endsWith('<div')); // false

复制字符串

  • repeat 复制指定字符串指定次数。
console.log('copy-'.repeat(3)); // copy-copy-copy-

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.