songstar0904 / es6 Goto Github PK
View Code? Open in Web Editor NEWlearn es6 by jspang
learn es6 by jspang
标准的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]
console.log(Array.form('foo'); // ["f", "o", "o"]
console.log(Array.from(new Set([1, 2, 3, 1, 3]))); // [1, 2, 3]
let m = new Map([[1, 2], [2, 4], [4, 8]]);
Array.from(m); // [[1, 2], [2, 4], [4, 8]]
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]
** 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
如果你需要找到一个元素的位置或者一个元素是否存在于数组中,使用Array.prototype.indexOf() 或 Array.prototype.includes()。
let binary = 0B010101;
console.log(binary); // 21
let b = 0o666;
console.log(b); // 438
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
console.log(Number.isNaN(NaN)); // true
console.log(Number.isInteger(1.2)); //false
console.log(Number.parseInt(1.2)); // 1
console.log(Number.parseFloat(1.2)); // 1.2
console.log(Number.MAX_SAFE_INTEGER);
console.log(Number.MIN_SAFE_INTEGER);
let a= Math.pow(2,53)-1;
console.log(Number.isSafeInteger(a));// false
Promise 对象用于一个异步操作的最终完成(或失败)及其结果值的表示。(简单点说就是处理异步请求。我们经常会做些承诺,如果我赢了你就嫁给我,如果输了我就嫁给你之类的诺言。这就是promise的中文含义:诺言,一个成功,一个失败。)
Promise
对象是由关键字 new
及其构造函数来创建的。该构造函数会?把一个叫做“处理器函数”(executor function)的函数作为它的参数。这个“处理器函数”接受两个函数——resolve
和 reject
——作为其参数。当异步任务顺利完成且返回结果值时,会调用 resolve
函数;而当异步任务失败且返回失败原因(通常是一个错误对象)时,会调用reject
函数。
{
let myPromise = new Promise((resolve, reject) => {
// 当异步代码执行成功是, 调用resolve(),失败调用reject()
// 我们使用setTimeout()来模拟异步代码
setTimeout(() => {
resolve('resolve成功');
}, 250);
});
myPromise.then((res) => {
console.log('收到信息:' + res); // 收到信息:resolve成功
})
}
fill() 方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。
let arr = [1, 2, 3, 4];
console.log(arr.fill('f', 1, 3)); // [1, "f", "f", 4]
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
}
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()方法返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 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'] ]
var obj = {foo: 'bar', baz: 42};
console.log(new Map(Object.entries(obj))); // Map(2) {"foo" => "bar", "baz" => 42}
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
这个关键字,,
隔开,否则会报错。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
方法是类的默认方法, 通过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
}
与函数一样, 类也可以使用表达式是形式定义。
{
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
而不是Me
, Me
只在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;
}
}
}
上面代码中, baz
和snaf
都是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
, 在类之外是读取不到这个属性的。 还可以看到私有属性与实例的属性可以同名的(#x
和get x()
)。#sum()
就是一个私有方法。
之所以要引入一个新的前缀#
表示私有属性,而没有采用private
关键字,是因为 JavaScript
是一门动态语言,使用独立的符号似乎是唯一的可靠方法,能够准确地区分一种属性是否为私有属性。
另外,私有属性也可以设置 getter
和 setter
方法。
类的方法内部如果含有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());
}
先建立一个项目的工程目录, 并在目录下便建立两个文件夹: src 和 dist
<!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>
在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"
}
可以根据自己的需要进行修改。
在终端中输入以下命令,如果你安装很慢的话,可以使用淘宝镜像的cnpm来进行安装。
cnpm install -g babel-cli
虽然已经安装了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文件, 并打开加入下面代码。
{
"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
来进行转换了。
由于本质上,ES6 的类只是 ES5 的构造函数的一层包装,所以函数的许多特性都被Class
继承,包括name
属性。
class Point {}
Point.name // Point
name属性总是返回紧跟在class关键字后面的类名。
当编写一个方法时, 我们允许它传入的参数是不确定的, 就可以使用对象扩展运算符作参数。
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运算符并不困难,它们有很多相似之处,甚至很多时候你不用特意去区分。它也用…(三个点)来表示,我们先来看一个例子。
function foo(first, ...arg){
console.log(arg);
}
foo(0, 1, 2, 3); // [1, 2, 3]
function foo(first, ...arg){
for(let val of arg) {
console.log(val);
}
}
foo(0, 1, 2, 3); // 1 2 3
for...of
循环可以避免我们开拓内存空间, 增加代码运行效率。
定义: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
Proxy 对象用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)。
let p = new Proxy(target, 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
}
{
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
}
Set
对象允许你存储任何类型的唯一值, 无论是原始值或者是对象引用。
所有Set实例继承自Set.prototype。
Set.prototype.constructor
Set.prototype.size
Set.prototype.add(value)
Set.prototype.clear()
Set.prototype.delete(value)
Set.prototype.has(value)
在这个操作前会返回的值(即如果该元素存在, 返回true, 否则返回false)。Set.prototype.entries()
[value, value]
数组。 为了使这个方法和Map对象保持相似, 每个值的键和值相等。Set.prototype.forEach(callbackFn[, thisArg])
callbackFn
。 如果提供了thisArg参数, 回调中的this为这个参数。Set.prototype.has(value)
Set.prototype.keys()
values()
方法相同,返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值。Set.prototype.values()
Set.prototype[@@iterator]()
{
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) {} 移除所有
}
{
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
}
}
// 转换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 对象允许你将弱保持对象存储在一个集合中。
{
let weakObj = new WeakSet();
let obj = {a: 1, b: 2};
weakObj.add(obj); // WeakSet {{…}}
}
这里需要注意的是,如果你直接在new 的时候就放入值,将报错。
WeakSet里边的值也是不允许重复的
Map 对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。
Object
和 Map
类似的一点就是它们都允许你按键存取一个值, 都可以删除键, 还可以检测一个键是否绑定值。区别在于:
prototype
键。 不过ES5 开始使用obj = Object.create(null)
来创建一个没有原型的对象。Symbols
, 但是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) {}
}
{
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'
}
{
let arr = [['key1', 'val1'], ['key2', 'val2']];
let myMap = new Map(arr);
myMap.get('key1'); // val1
console.log(JSON.stringify([...myMap])); // [["key1","val1"],["key2","val2"]]
}
箭头函数表达式的语法比函数表达式更短, 并且不绑定自己的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
相关的关泽都被忽略。var f = () => {'user strict'; return this};
f() === window; // true
var b = function() {
'use strict';
return this;
}
b(); // undefined
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
对象。 因此, 参数只是在封闭范围内引用相同的名称。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
一起使用会抛出错误var Foo = () => {};
var foo = new Foo(); // TypeError: Foo is not a constructor
var Foo = () => {};
console.log(Foo.prototype); // undefined
用的最多的变量声明方式, 但同时也有一定的问题。 我们都知道在ES6之前,JavaScript没有真正意义上的块级作用域,正是因为只能通过var 来声明变量。这样声明可能出现污染某些变量。比如:
var i = 1;
// ...
for(var i = 0; i < 5; i++){
// ...
}
console.log(i); // 5
这是一个最常见的栗子🌰, 在全局声明了一个i为1, 经过一个for循环后, i的值产生了变化。
window.变量名
的方式访问。for(let x...)
的循环每次迭代时都为x创建新的绑定。(某些场景代替闭包){
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 obj = {'a': 1, 'b': 2};
obj.a = 3;
console.log(obj.a); // 3
obj = {}; // TypeError
let name = 'foo';
let fun = 'bar';
var obj = {name, fun};
console.log(obj); // {name: "foo", fun: "bar"}
有时候我们会在后台取出key值, 而不是我们前台定义好的, 这时候我们如何构建我们的key值呢?
let key = 'skill';
var obj = {
[key]: 'web'
};
console.log(obj); // {skill: "web"}
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"}
在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);
所有字符串放入单引号里面```, 而变量放在${}
中, 且`${}`中可以作运算。
console.log(sayHtml.indexOf('div')); // 1
console.log(sayHtml.includes('div')); // true
console.log(sayHtml.startsWith('<div')); // true
console.log(sayHtml.endsWith('<div')); // false
console.log('copy-'.repeat(3)); // copy-copy-copy-
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.