let
声明的变量只在所处的块级有效,var
不具有这个特点for
循环:循环变量是一个父作用域,而循环体内部是一个单独的子作用域。
let
命令,不再受外部的影响。let
命令声明变量之前,该变量都是不可用的var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
规定暂时性死区和let
、const
语句不出现变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为
ES6 规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用
避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。
// 块级作用域内部的函数声明语句,建议不要使用
{
let a = 'secret';
function f() {
return a;
}
}
// 块级作用域内部,优先使用函数表达式
{
let a = 'secret';
let f = function () {
return a;
};
}
ES6 的块级作用域必须有大括号,如果没有大括号,JavaScript 引擎就认为不存在块级作用域。
const声明一个只读的常量。一旦声明,常量的值就不能改变。
注意
Object.freeze
方法//将对象彻底冻结的函数
var constantize = (obj) => {
Object.freeze(obj);
Object.keys(obj).forEach( (key, i) => {
if ( typeof obj[key] === 'object' ) {
constantize( obj[key] );
}
});
};
var function let const import class
var
命令和function
命令声明的全局变量,依旧是顶层对象的属性;
let
命令、const
命令、class
命令声明的全局变量,不属于顶层对象的属性。
在各种环境下取到顶层对象
(typeof window !== 'undefined'
? window
: (typeof process === 'object' &&
typeof require === 'function' &&
typeof global === 'object')
? global
: this);
// 方法二
var getGlobal = function () {
if (typeof self !== 'undefined') {
return self; }
if (typeof window !== 'undefined') {
return window; }
if (typeof global !== 'undefined') {
return global; }
throw new Error('unable to locate global object');
};
1. 基本用法
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
...
展开运算符,剩余运算符
let [a, b, c] = [1, 2, 3];
如果解构不成功,变量的值就等于undefined
。
对于 Set 结构,也可以使用数组的解构赋值。
let [x, y, z] = new Set(['a', 'b', 'c']);
x // "a"
事实上,只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值。
2.默认值
注意,ES6 内部使用严格相等运算符(===
),判断一个位置是否有值。所以,只有当一个数组成员严格等于undefined
,默认值才会生效。
默认值可以引用解构赋值的其他变量,但该变量必须已经声明。
let [x = 1, y = x] = []; // x=1; y=1
1.简介
如果变量名与属性名不一致,必须写成下面这样。
let {
foo: baz } = {
foo: 'aaa', bar: 'bbb' };
baz // "aaa"
对象的解构赋值是下面形式的简写,也就是说,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。
let {
foo: foo, bar: bar } = {
foo: 'aaa', bar: 'bbb' };
上面代码中,foo是匹配的模式,baz才是变量。真正被赋值的是变量baz,而不是模式foo。
注意,对象的解构赋值可以取到继承的属性。
const obj1 = {
};
const obj2 = {
foo: 'bar' };
Object.setPrototypeOf(obj1, obj2);
const {
foo } = obj1;
foo // "bar"
Object.setPrototypeOf()
方法设置一个指定的对象的原型到另一个对象或null。
2. 默认值
默认值生效的条件是,对象的属性值严格等于undefined。
解构赋值允许等号左边的模式之中,不放置任何变量名。因此,可以写出非常古怪的赋值表达式。
({
} = [true, false]);
({
} = 'abc');
({
} = []);
3.由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构。
let arr = [1, 2, 3];
let {
0 : first, [arr.length - 1] : last} = arr;
first // 1
last // 3
字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象。
const [a, b, c, d, e] = 'hello';
类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值。
let {
length : len} = 'hello';
len // 5
解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。
let {
toString: s} = 123;
s === Number.prototype.toString // true
let {
toString: s} = true;
s === Boolean.prototype.toString // true
上面代码中,数值和布尔值的包装对象都有toString属性,因此变量s都能取到值。
解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。
由于undefined
和null
无法转为对象,所以对它们进行解构赋值,都会报错。
let {
prop: x } = undefined; // TypeError
let {
prop: y } = null; // TypeError
函数的参数也可以使用解构赋值。
function add([x, y]){
return x + y;
}
add([1, 2]); // 3
函数参数的解构也可以使用默认值。
1.不能使用圆括号的情况
(1)变量声明语句
(2)函数参数
(3)赋值语句模式
2.可以使用圆括号的情况
可以使用圆括号的情况只有一种:赋值语句的非模式部分,可以使用圆括号。
(1)交换变量的值
(2)从函数返回多个值
(3)函数参数的定义、
(4)提取 JSON 数据
(5)函数参数的默认值
jQuery.ajax = function (url, {
async = true,
beforeSend = function () {
},
cache = true,
complete = function () {
},
crossDomain = false,
global = true,
// ... more config
} = {
}) {
// ... do stuff
};
(6)遍历 Map 结构
(7)输入模块的指定方法
加载模块时,往往需要指定输入哪些方法。解构赋值使得输入语句非常清晰。
const {
SourceMapConsumer, SourceNode } = require("source-map");
允许采用\uxxxx
形式表示一个字符,其中xxxx表示字符的 Unicode 码点。
这种表示法只限于码点在\u0000~\uFFFF
之间的字符。超出这个范围的字符,必须用两个双字节的形式表示。
ES6 对这一点做出了改进,只要将码点放入大括号,就能正确解读该字符。
有了这种表示法之后,JavaScript 共有 6 种方法可以表示一个字符。
'\z' === 'z' // true
'\172' === 'z' // true
'\x7A' === 'z' // true
'\u007A' === 'z' // true
'\u{7A}' === 'z' // true
ES6 为字符串添加了遍历器接口,使得字符串可以被for…of循环遍历。
for (let codePoint of 'foo') {
console.log(codePoint)
}
// "f"
// "o"
// "o"
ES2019 允许 JavaScript 字符串直接输入 U+2028(行分隔符)和 U+2029(段分隔符)。
为了确保返回的是合法的 UTF-8 字符,ES2019 改变了JSON.stringify()的行为。
如果遇到0xD800到0xDFFF之间的单个码点,或者不存在的配对形式,它会返回转义字符串,留给应用自己决定下一步的处理。
模板字符串(template string)是增强版的字符串,用反引号(`)标识。
它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
模板字符串中嵌入变量,需要将变量名写在${}之中。
大括号内部可以放入任意的 JavaScript 表达式,可以进行运算,以及引用对象属性。
如果需要引用模板字符串本身,在需要时执行,可以写成函数。
let func = (name) => `Hello ${
name}!`;
func('Jack') // "Hello Jack!"
let name = 'zpp';
let sex = 'female';
function fn() {
console.log(arguments);
/*
0: (3) ["名字是:", "性别", "", raw: Array(3)]
1: "zpp"
2: "female"
*/
}
// function fn(arr, ...args) {
// console.log(args);//(2) ["zpp", "female"]
// console.log(arr);//(3) ["名字是:", "性别", "", raw: Array(3)]
// }
let str = fn`名字是:${
name}性别${
sex}`;
//includes startWith endWith padStart padEnd //不够的话,在前后补零
console.log('djsk'.includes('dj'));
console.log("3".padStart(2, 0));
ES6 提供了String.fromCodePoint()
方法,可以识别大于0xFFFF的字符,用于从 Unicode 码点返回对应字符。
在作用上,正好与下面的codePointAt()
方法相反。
String.fromCodePoint(0x20BB7)
// ""
该方法返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串,往往用于模板字符串的处理方法。
String.raw`Hi\n${
2+3}!`
// 实际返回 "Hi\\n5!",显示的是转义后的结果 "Hi\n5!"
codePointAt()
方法会正确返回 32 位的 UTF-16 字符的码点。
对于那些两个字节储存的常规字符,它的返回结果与charCodeAt()方法相同。
includes():返回布尔值,表示是否找到了参数字符串。
startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
使用第二个参数n时,endsWith的行为与其他两个方法有所不同。它针对前n个字符,而其他两个方法针对从第n个位置直到字符串结束。
repeat方法返回一个新字符串,表示将原字符串重复n次。
如果某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全。
trimStart()消除字符串头部的空格,trimEnd()消除尾部的空格。它们返回的都是新字符串,不会修改原始字符串。
matchAll()方法返回一个正则表达式在当前字符串的所有匹配。
1. 基本用法
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello') // Hello World
函数的参数默认已经定义了,不能再用let,const声明
function show(a = 18) {
let a = 101;// 错误
console.log(a)
}
使用参数默认值时,函数不能有同名参数。
参数默认值不是传值的,而是每次都重新计算默认值表达式的值。也就是说,参数默认值是惰性求值的。
2. 与解构赋值默认值结合使用
function foo({
x, y = 5} = {
}) {
console.log(x, y);
}
foo() // undefined 5
上面代码指定,如果没有提供参数,函数foo的参数默认为一个空对象。
3. 参数默认值的位置
通常情况下,定义了默认值的参数,应该是函数的尾参数。
有默认值的参数都不是尾参数。这时,无法只省略该参数,而不省略它后面的参数,除非显式输入undefined。
function foo(x = 5, y = 6) {
console.log(x, y);
}
foo(undefined, null)
4. 函数的 length 属性
length属性的返回值,等于函数的参数个数减去指定了默认值的参数个数。
如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了。
(function (a = 0, b, c) {
}).length // 0
(function (a, b = 1, c) {
}).length // 1
5. 作用域
一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。等到初始化结束,这个作用域就会消失。这种语法行为,在不设置参数默认值时,是不会出现的。
如果参数的默认值是一个函数,该函数的作用域也遵守这个规则。
6 .应用
利用参数默认值,可以指定某一个参数不得省略,如果省略就抛出一个错误。
另外,可以将参数默认值设为undefined,表明这个参数是可以省略的。
let arr = ['appele', 'banana', 'orange'];
console.log(...arr); //appele banana orange
function show(...a) {
console.log(a); //(5) [1, 2, 3, 9, 8]
console.log(a.sort());//(5) [1, 2, 3, 8, 9]
}
show(1, 2, 3, 9, 8)
function show(a, ...b) {
console.log(a); //1
console.log(b); //(4) [2, 3, 4, 5]
}
show(1, 2, 3, 4, 5);
Array.from(arr)
: 把类数组对象(获取的一组对象,arguments…)转成数组
let arr = [1, 2, 3, 4, 5];
//1
let arr2 = [...arr];
//2
let arr2 = Array.from(arr);
如果将一个匿名函数赋值给一个变量,ES5 的name属性,会返回空字符串
而 ES6 的name属性会返回实际的函数名。
var f = function () {
};
// ES5
f.name // ""
// ES6
f.name // "f"
1. 基本用法
左边是参数,右边是返回值
如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。
如果箭头函数只有一行语句,且不需要返回值,不用写大括号了。
()=>{
}
2. 注意点
(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
3. 不适用的场合
第一个场合是定义对象的方法,且该方法内部包括this。
第二个场合是需要动态this的时候,也不应使用箭头函数。
ES2017 允许函数的最后一个参数有尾逗号(trailing comma)。
修改后的toString()方法,明确要求返回一模一样的原始代码。
ES2019 做出了改变,允许catch语句省略参数。
1. for
循环
let arr = ['apple', 'banana', 'orange', 'tomato'];
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
2. arr.forEach
循环
arr.forEach
(回调函数,this指向谁)
//代替普通的for循环,没有返回值
arr.forEach(function (val, index, array) {
})
3. arr.map
循环
正常情况下需要配合return,没有return的相当于forEach
作用:重新整理数据结构
4. arr.filter()
循环
过滤,过滤不合格的元素,如果回调函数返回true就保留,否则丢弃
let arr = [
{
title: 'aaa1', read: 100, hot: true },
{
title: 'aaa2', read: 100, hot: true },
{
title: 'aaa3', read: 100, hot: false },
{
title: 'aaa4', read: 100, hot: true },
{
title: 'aaa5', read: 100, hot: false },
{
title: 'aaa6', read: 100, hot: true },
]
let newArr = arr.filter((item, index, arr) => {
return item.hot;
})
console.log(newArr)
5. arr.some()
循环
类似查找,数组中里面的某一个元素符合条件,返回true
let arr = ['apple', 'banana', 'orange', 'tomato'];
let b = arr.some((val, index, arr) => {
return val == 'banana';
});
console.log(b); //false
6. arr.every()
循环
// 类似查找,数组中里面的每一个元素都符合条件,返回true
// arr.every()
function findInArray(arr,item){
return arr.some((val,index,arr)=>{
return val == item;
});
}
console.log(findInArray(arr,'orange2')); //数组中有没有orange2这个单词
7. arr.reduce()
做一个阶乘。。。
tip:2的3次方 Math.pow(2,3)
或者2**3
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// prev 上一次返回的结果 cur 当前的值
let res = arr.reduce((prev, cur, index, arr) => {
return prev + cur;
})
console.log(res)
8. arr.reduceRight()
从右往左
9. for .. of
循环
arr.keys() 数组的下标
arr.entries() 数组的某一项
let arr = ['apple', 'banana', 'orange', 'tomato'];
for (let val of arr) {
console.log(val);
}
/*
apple
banana
orange
tomato
*/
for (let index of arr.keys()) {
console.log(index);
}
/*
0
1
2
3
*/
for (let item of arr.entries()) {
console.log(item);
}
/*
(2) [0, "apple"]
(2) [1, "banana"]
(2) [2, "orange"]
(2) [3, "tomato"]
*/
for (let [key, val] of arr.entries()) {
console.log(key, val);
}
1. Array.from(arr)
:
把类数组对象(获取的一组对象,arguments…)转成数组
2. Array.of()
:
把一组值转换为数组 Array.of('aaa','bbb','ccc')
3. arr.find()
:查找
找到第一个符合条件的数组成员,如果没找到返回undefined
4. arr.findIndex()
:
找得是位置,没有找到返回-1
5. arr.fill()
:填充
arr.fill(填充的东西,开始的位置,结束的位置)
let arr = new Array(10);
arr.fill('默认值', 1, 3)
console.log(arr);
6. arr.include
数组中是否包含某一个成员
let arr = ['apple','banana','tomato'];
let b = arr.include('orange2');
console.log(b); //false
对象有简洁的语法
不要使用箭头函数,因为对象的大括号并不是一个块级的作用域,this会指向window
let name = 'zpp';
let age = 18;
let json = {
name, //name :name
age, //age:age
// showA:function(){
// return this.name;
// },
showA(){
return this.name;
}
};
console.log(json)
Object.is()
:用来比较两个值是否相等
Object.is(NaN,NaN)
Object.assign()
: 用来合并对象
作用:
(1)合并参数:多用于ajax接受用户的参数,用户没有写参数时,用默认的,如果写的话,就覆盖默认值
(2)复制一个对象
let json = {
a: 1 };
let json2 = {
b: 2, a: 2 };
let json3 = {
c: 2 };
let obj = Object.assign({
}, json, json2, json3);
console.log(obj);
//{a: 2, b: 2, c: 2} 后面的胡覆盖前面
Object.keys()
Object.entries();
Object.values();
let {
keys, values, entries } = Object;
//解构
let json = {
a: 1,
b: 2,
c: 3
}
for (let key of keys(json)) {
console.log(key);
}
for (let value of values(json)) {
console.log(value);
}
for (let item of entries(json)) {
console.log(item);
}
for (let [key, val] of entries(json)) {
console.log(key, val);
}
let json = {
a:3,b:4};
let json2 = {
...json}
有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。
ES6 规定,Promise对象是一个构造函数,用来生成Promise实例。
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。
promise.then(function(value) {
// success
}, function(error) {
// failure
});
补充
setTimeout()
方法用于在指定的毫秒数后调用函数或计算表达式。
setTimeout(code, milliseconds, param1, param2, ...)
setTimeout(function, milliseconds, param1, param2, ...)
下面是一个用Promise对象实现的 Ajax 操作的例子。
const getJSON = function(url) {
const promise = new Promise(function(resolve, reject){
const handler = function() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
const client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
});
return promise;
};
getJSON("/posts.json").then(function(json) {
console.log('Contents: ' + json);
}, function(error) {
console.error('出错了', error);
});
执行的顺序
Promise 新建后立即执行,所以首先输出的是Promise。然后,then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出。
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
// Promise
// Hi!
// resolved
立即 resolved 的 Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务
new Promise((resolve, reject) => {
resolve(1);
console.log(2);
}).then(r => {
console.log(r);
});
// 2
// 1
它的作用是为 Promise 实例添加状态改变时的回调函数。
Promise.prototype.catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。
getJSON('/posts.json').then(function(posts) {
// ...
}).catch(function(error) {
// 处理 getJSON 和 前一个回调函数运行时发生的错误
console.log('发生错误!', error);
});
finally()
方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。
promise
.then(result => {
···})
.catch(error => {
···})
.finally(() => {
···});
Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.all([p1, p2, p3]);
必须确保,所有的promise对象都是resolve状态,都是成功状态
Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.race([p1, p2, p3]);
只要有一个成功就可以
将现有对象转为 Promise 对象,Promise.resolve()方
法就起到这个作用。
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))
p.then(null, function (s) {
console.log(s)
});
// 出错了
加载图片
const preloadImage = function (path) {
return new Promise(function (resolve, reject) {
const image = new Image();
image.onload = resolve;
image.onerror = reject;
image.src = path;
});
};
function loadImageAsync(url) {
return new Promise(function(resolve, reject) {
const image = new Image();
image.onload = function() {
resolve(image);
};
image.onerror = function() {
reject(new Error('Could not load image at ' + url));
};
image.src = url;
});
}
import https://code.jquery .com/jgquery-3.3.1js;
import './modules/1js';
如果这么用,相当于引入文件模块功能主要由两个命令构成:export和import。
export
命令用于规定模块的对外接口
import
命令用于输入其他模块提供的功能。
使用:
// profile.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export {
firstName, lastName, year };
function v1() {
... }
function v2() {
... }
export {
v1 as streamV1,
v2 as streamV2,
v2 as streamLatestVersion
};
import {
lastName as surname } from './profile.js';
使用整体加载,即用星号(*
)指定一个对象,所有输出值都加载在这个对象上面。
import * as circle from './circle';
为模块指定默认输出。
export default命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,因此export default命令只能使用一次。所以,import命令后面才不用加大括号,因为只可能唯一对应export default命令。
// export-default.js
export default function () {
console.log('foo');
}
其他模块加载该模块时,import命令可以为该匿名函数指定任意名字。
// import-default.js
import customName from './export-default';
customName(); // 'foo'
本质上,export default就是输出一个叫做default的变量或方法,所以它后面不能跟变量声明语句。
// 正确
export var a = 1;
// 正确
var a = 1;
export default a;
// 错误
export default var a = 1;
如果想在一条import语句中,同时输入默认方法和其他接口,可以写成下面这样。
import _, {
each, forEach } from 'lodash';
如果在一个模块之中,先输入后输出同一个模块,import语句可以与export语句写在一起。
export {
foo, bar } from 'my_module';
// 可以简单理解为
import {
foo, bar } from 'my_module';
export {
foo, bar };
// 接口改名
export {
foo as myFoo } from 'my_module';
// 整体输出
export * from 'my_module';
import()函数可以用在任何地方,不仅仅是模块,非模块的脚本也可以使用。它是运行时执行,也就是说,什么时候运行到这一句,就会加载指定的模块。
另外,import()函数与所加载的模块没有静态连接关系,这点也是与import语句不相同。
import()类似于 Node 的require方法,区别主要是前者是异步加载,后者是同步加载。
import('./myModule.js')
.then(({
export1, export2}) => {
// ...·
});
Promise.all([
import('./module1.js'),
import('./module2.js'),
import('./module3.js'),
])
.then(([module1, module2, module3]) => {
···
});
async function main() {
const myModule = await import('./myModule.js');
const {
export1, export2} = await import('./myModule.js');
const [module1, module2, module3] =
await Promise.all([
import('./module1.js'),
import('./module2.js'),
import('./module3.js'),
]);
}
main();
es5中用function来写一个class
function Person(name, age) {
this.name = name;
this.age = age;
}
var person = new Person('Strive', 18);
Person.prototype.showName = function () {
console.log(this.name);
}
person.showName();
es6 中的class
class Person {
constructor(name, age) {
//构造方法,只要调用了new就会执行
//console.log(`构造函数执行了,${name},${age}`);
this.name = name;
this.age = age;
}
showName() {
return `名字为:${
this.name}`;
}
showAge() {
return `年龄为:${
this.age}`;
} //不需要加,
}
let p1 = new Person('zpp', 18);
console.log(p1.showName(), p1.showAge());
注意:es6中class没有提升功能,es5用函数模拟是可以提升的
class里面的取值(get)和存值函数(set)
静态的方法:就是类上的方法
static aaa() {
return `静态的方法`
}
父类.aaa();
继承:
class Student extends Person{
}
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
注意,定义“类”的方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。另外,方法之间不需要逗号分隔,加了会报错。
class B {
}
let b = new B();
b.constructor === B.prototype.constructor // true
上面代码中,b是B类的实例,它的constructor方法就是B类原型的constructor方法。
Point.prototype.constructor === Point // true
prototype对象的constructor属性,直接指向“类”的本身,这与 ES5 的行为是一致的
类的内部所有定义的方法,都是不可枚举的(non-enumerable)。
class Point {
constructor(x, y) {
// ...
}
toString() {
// ...
}
}
Object.keys(Point.prototype)
// []
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]
2. constructor方法
constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。
3. 类的实例
生成类的实例的写法,与 ES5 完全一样,也是使用new
命令。
4. 取值和存值函数
与 ES5 一样,在“类”的内部可以使用get和set关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。
class MyClass {
constructor() {
// ...
}
get prop() {
return 'getter';
}
set prop(value) {
console.log('setter: '+value);
}
}
let inst = new MyClass();
inst.prop = 123;
// setter: 123
inst.prop
// 'getter'
5.属性表达式
类的属性名,可以采用表达式。
let methodName = 'getArea';
class Square {
constructor(length) {
// ...
}
[methodName]() {
// ...
}
}
上面代码中,Square
类的方法名getArea
,是从表达式得到的。
6. class表达式
与函数一样,类也可以使用表达式的形式定义。
const MyClass = class Me {
getClassName() {
return Me.name;
}
};
如果类的内部没用到的话,可以省略类的名称,也就是可以写成下面的形式。
采用 Class 表达式,可以写出立即执行的 Class。
7. 注意点
(1)严格模式
(2)不存在变量提升
(3)name属性
(4)Generator 方法
如果某个方法之前加上星号(*),就表示该方法是一个 Generator 函数。
(5)this 的指向
类的方法内部如果含有this,它默认指向类的实例。
class Logger {
printName(name = 'there') {
this.print(`Hello ${
name}`);
}
print(text) {
console.log(text);
}
}
const logger = new Logger();
const {
printName } = logger;
printName(); // TypeError: Cannot read property 'print' of undefined
上面代码中,printName方法中的this,默认指向Logger类的实例。但是,如果将这个方法提取出来单独使用,this会指向该方法运行时所在的环境(由于 class 内部是严格模式,所以 this 实际指向的是undefined),从而导致找不到print方法而报错。
一个比较简单的解决方法是,在构造方法中绑定this,这样就不会找不到print方法了。
class Logger {
constructor() {
this.printName = this.printName.bind(this);
}
// ...
}
如果在一个方法前,加上static
关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
注意,如果静态方法包含this关键字,这个this指的是类,而不是实例。
class Foo {
static bar() {
this.baz();
}
static baz() {
console.log('hello');
}
baz() {
console.log('world');
}
}
Foo.bar() // hello
父类的静态方法,可以被子类继承。
静态方法也是可以从super对象上调用的。
实例属性除了定义在constructor()方法里面的this上面,也可以定义在类的最顶层。
class IncreasingCounter {
_count = 0;
get value() {
console.log('Getting the current value!');
return this._count;
}
increment() {
this._count++;
}
}
静态属性指的是 Class 本身的属性,即Class.propName,而不是定义在实例对象(this)上的属性。
class Foo {
}
Foo.prop = 1;
Foo.prop // 1
new是从构造函数生成实例对象的命令。ES6 为new命令引入了一个new.target属性,该属性一般用在构造函数之中,返回new命令作用于的那个构造函数。如果构造函数不是通过new命令或Reflect.construct()调用的,new.target会返回undefined,因此这个属性可以用来确定构造函数是怎么调用的
Class 内部调用new.target,返回当前 Class。
class Rectangle {
constructor(length, width) {
console.log(new.target === Rectangle);
this.length = length;
this.width = width;
}
}
var obj = new Rectangle(3, 4); // 输出 true
数据类型: number,string,boolean,Object,undefined,function
用typeof检测出来的数据类型是:symbol
定义
let syml = Symbol('aaa');
注意:
箭头函数
()=>{}
function * show(){
yield;
}
定义:
function* gen() {
yield 'welcom';
yield 'zpp';
return 'haha'
}
调用:
let g1 = gen();
console.log(g1.next());
//{value: "welcom", done: false} false 指的是还没有完成,后面还有任务
console.log(g1.next());
//{value: "zpp", done: false}
console.log(g1.next());
//{value: "haha", done: true}
console.log(g1.next());
//{value: undefined, done: true}
上述调用太过复杂
可以通过for … of自动遍历generation(return的东西不会遍历出)
for (let val of g1) {
console.log(val); //return的东西不会遍历
//welcom
//zpp
}
还可以
let [a, b] = gen();
console.log(a, b);
//welcom zpp
console.log(...gen());
//welcom zpp
Array.from()
console.log(Array.from(gen()));
//(2) ["welcom", "zpp"]
应用:generation配合axios来进行简单的数据请求
https://github.com/axios/axios
https://api.github.com/users
异步的generator一般要配合promise来使用
function* gen() {
let val = yield '2763899039zpp';
yield axios.get(`https://api.github.com/users/${
val}`);
}
let g1 = gen();
let username = g1.next().value;
g1.next(username).value.then(res => {
console.log(res.data);
})
异步:不连续,上一个操作没有完成,下一个曹所照样开始
同步:连续,上一个操作没有完成,下一个没法开始
关于异步,解决
async function fn(){
//表示异步,这个函数里面有异步的任务
let result = await xxx //表示后面的结果需要等待
}
async特点
1.await只能放到async函数中
2.相比generator语义化更强
3.await后面可以是promise对象,也可以是其他类型
4.async函数返回的是一个promise对象
5.只要await语句后面promise状态变成了reject,那么整个async函数留会中断了(可以使用try…catch)
数据结构
- 数组
- json,二叉树
set
数据结构:类似数组,但是里面没有重复值
let arr = [‘a’,‘a’,‘b’]
let arr = new Array();
set
用法:
1.new Set(['a','b','c']);
2.setArr.add('e')
:添加
3.setArr.delete('b'))
:删除一箱
4.setArr.has('a')
:判断是否存在‘a’这一项
let setArr = new Set(['a', 'b', 'c']);
console.log(setArr);
console.log(setArr.delete('b'));
console.log(setArr.has('a')); //true
5.属性:setArr.size
元素个数
6.方法:setArr.clear()
清空
循环:默认是values
let setArr = new Set(['a', 'b', 'c', 'd', 'e']);
for (let item of setArr) {
console.log(item);
}
//a b c d e
console.log('-----------------------------')
for (let item of setArr.keys()) {
console.log(item);
}
//a b c d e
console.log('-----------------------------')
for (let item of setArr.values()) {
console.log(item);
}
//a b c d e
console.log('-----------------------------')
for (let item of setArr.entries()) {
console.log(item);
}
setArr.forEach((v,k)=>{
console.log(v,k)
})
add
返回的是自己
setArr.add('e').add('f').add('g');
set
的用处:数组去重
//数组去重
let arr = [1, 1, 2, 3, 4, 5, 5, 6, 7, 8, 7, 6, 2];
let newArr = [...new Set(arr)];
console.log(newArr);
//(8) [1, 2, 3, 4, 5, 6, 7, 8]
注意点:
new Set([])
存储数组
如果用add
的方式添加对象的话是没有问题的,但是不能直接给Set中添加对象
new WeakSet({})
存储json
初始往里面添加Json是不可以的,最好用add添加,没有size()
属性
类似json,但是json的键(key)只能是字符串,map的值可以是任意的类型
使用:
1.let map = new Map();
2.map.set(key,value);
设置一个值
3.map.get(key)
获取一个值
4.map.delete(key)
删除一个值
5.map.has(key)
判断有没有这个值
6.map.clear(key)
清空
let map = new Map();
map.set('a', 'aaa');
console.log(map);
//Map(1) {"a" => "aaa"}
console.log(map.get("a"));
//aaa
循环:和set一样
key只能是对象
总结:
二进制:(Binary)
let a = 0b010101;
八进制:(Octal)
let a = 0o666
十六进制:
#ccc
Number(),paseInt,paseFloat()
Number.isNaN(NaN)
->true
Number.isFinite(a)
用来检查一个数值是否为有限的(finite)
Number.isInteger()
判断数字是不是整数
Number.paseInt();
Number.paseFloat();
安全整数Number.isSafeInteger(a);
[ -(2^ 53-1),(2^ 53-1)]
Number.MAX_SAFE_INTEGER;
最大安全整数
Number.MIN_SAFE_INTEGER;
最小安全整数
let a = 2 ** 53;
Number.isSafeInteger(a);
方法 | 说明 |
---|---|
Math.trunc |
截取,只保留整数部分 方法用于去除一个数的小数部分,返回整数部分。 |
Math.sign |
判断一个数是正数、负数、0 |
Math.cbrt() |
方法用于计算一个数的立方根。 |
ES2016 新增了一个指数运算符(**
)
?<名字>
let str = '2018-01-20';
let reg = /(\d{4})-(\d{2})-(\d{2})/
console.log(str.match(reg));
//(4) ["2018-01-20", "2018", "01", "20", index: 0, input: "2018-01-20", groups: undefined]
let dataArr = str.match(reg);
let year = dataArr[1];
let month = dataArr[2];
let day = dataArr[3];
console.log(year, month, day);
//2018 01 20
(2)用命名捕获来拆分日期,可以获得json格式
let reg1 = /(?\d{4})-(?\d{2})-(?\d{2})/
console.log(str.match(reg1).groups);
//{year: "2018", month: "01", day: "20"}
\k
let reg = /^(?welcome)-\k$/ ;
let str = 'a-a';
let str2 = 'strive-strive'
let str3 = 'welcome-welcome'
console.log(reg.test(str)); //false
console.log(reg.test(str2)); //false
console.log(reg.test(str3)); //true
\1
和\k
都可以
let reg2 = /^(?welcome)-\k-\1$/ ;
let str4 = 'welcome-welcome-welcome';
console.log(reg2.test(str4)); //true
$
let str = '2018-01-20'
let reg = /(?\d{4})-(?\d{2})-(?\d{2})/
console.log(str.match(reg).groups);
//{year: "2018", month: "01", day: "20"}
console.log(str.replace(reg, '$/$/$' ));
//2018/01/20
用函数来处理
let str1 = str.replace(reg, (...args) => {
let {
year, month, day } = args[args.length - 1];
return `${
day}/${
month}/${
year}`
})
console.log(str1);
//20/01/2018
之前‘.
’在正则里面表示匹配任意的东西,但是不包括\n
funtion fn(){
}
fn() //这样调用是普通函数
fn `` //标签函数使用
proxy:代理·
扩展(增强)对象的一些功能
proxp作用:预警
上报、扩展功能、统计、增强对象
proxy是设计模式的一种,代理模式
语法:
new Proxy(target,handler);
let obj = new Proxy(被代理的对象,对代理的对象做什么操作);
handler:
{
get(){} //获取的时候干得事情
set(){} //设置的时候干得事情
deleteProperty(){} //删除的时候干得事情
has() //是否有这个东西
apply() //调用函数
}
let obj = {
name: 'zpp',
}
let newObj = new Proxy(obj, {
get(target, property) {
if (property in target) {
//如果该属性在这个对象上
return target[property];
}
else {
throw new ReferenceError(`${
property}属性不在此对象上`)
}
}
})
console.log(newObj.age
案例二:创建元素
const DOM = new Proxy({
}, {
get(target, property) {
return function (attr = {
}, ...children) {
const el = document.createElement(property);
for (let key of Object.keys(attr)) {
el.setAttribute(key, attr[key]);
}
for (let child of children) {
if (typeof child == 'string') {
child = document.createTextNode(child);
}
el.appendChild(child);
}
return el;
}
}
})
//let oDiv = DOM.div({ id: 'div', class: 'aaa' }, '我是div', 'aaa')
//console.log(oDiv)
let oDiv = DOM.div(
{
id: 'div', class: 'aaa' }, '我是div', '哈哈',
DOM.a({
href: 'http://baidu.com' }, '访问百度'),
DOM.ul({
},
DOM.li({
}, '1111'),
DOM.li({
}, '2222'),
DOM.li({
}, '3333'),
DOM.li({
}, '4444'),
)
)
window.onload = function () {
document.body.appendChild(oDiv);
}
let obj = new Proxy({
}, {
set(target, prop, value) {
if (prop == 'age') {
if (!Number.isInteger(value)) {
throw new TypeError(`年龄必须为整数`)
}
if (value > 200) {
throw new RangeError('年龄超标了,必须小于200岁')
}
}
}
})
obj.a = 123;
obj.name = 'zpp';
obj.age = 201;
案例四:has
和deleteProperty
的使用
let json = {
a: 1,
b: 2
}
let newJson = new Proxy(json, {
deleteProperty(target, property) {
console.log(`你要删除${
property}属性`);
delete target[property];
},
has(target, property) {
console.log(`判断是否调用has方法`);
//todo
}
})
'a' in newJson;
delete newJson.a;
console.log(newJson)
案例五:apply
的使用
Reflect.apply()
反射,可以将原来的函数返回出去
Reflect.apply(调用夫人函数,this指向,参数数组)
let res = Reflect.apply(Math.ceil, null, [9.8])
console.log(res);
//10
Reflect
:反射
fn.call()
fn.apply() 类似
function show(...args) {
console.log(this);
console.log(args)
}
show(1, 2, 3, 4);
show.call('abc', 1232, 324);
Reflect.apply(show, 'aaaa', [1, 2, 3, 4]);