参考阮一峰的ES6文档(http://es6.ruanyifeng.com/#docs/let)
一:let 和 const 命令
1 let 命令
<1> 用于声明变量,但声明的变量只在当前代码块有效(块级作用域),即作用域在 { } 内
<2> 不存在变量提示,即:无预解析
<3> 暂时性死区,即:只要 **块级作用域 ** 中存在 let 命令,那么他所声明的变量就"绑定"到当前区域,不在受外部的影响,若 let 声明的变量在此之前使用,则会报错
<4> 不允许在同一个作用域中多次声明
<5> 【注:】ES5 只有函数作用域 和 全局作用域,没有块级作用域
var tem = 123;
if(true){
console.log(tem); // 报错
let tem = 0;
}
let a = a ; // 报错
let b = 0;
let b = 1; // 报错
2 函数声明
【注】ES5 中 不允许块级作用域中声明函数,但为了兼容老代码,基本上所有的浏览器都无视了这一个规定
ES6 中 允许块级作用域中声明函数,但是函数的声明相当于 let 声明一样,只作用于当前的块级作用域,但是为了兼容老代码,所以ES6的规定中浏览器可以不遵守此规定,有自己的行为方式,所以一般函数的声明会像 var 一样会进行函数名的提升
【注】ES6 中 块级作用域中函数的声明必须包含在 { } 中,如 if(true) f(){}; 会报错
3 const 命令
<1> 用于声明一个常量的值,一旦声明,常量的值就不能改变,若改变则会报错
<2> 声明时必须赋值,若只声明不赋值,就会报错
<3> 作用域与 let 相同,只存在于当前作用域中
<4> 没有预解析
<5> 存在暂时性死区的特性
<6> 同一个变量不能多次声明
本质:const实际上保证的是变量指向的那个内存地址所保存的值是不能改变的,所以:
当保存为基本类型时,内存地址保存的就是一个值,此时就为常量
当保存为复杂类型时,内存地址保存的就是一个地址,即:指针,他只能保证这个地址不能改变,但不能保证这个地址指向堆中的数据不能改变,所以,将对象保存为常量时需小心使用
const PI = 3.141592654;
console.log(PI);
PI = 3 ; // 报错
const a ; //报错
const b = [];
b.push('hello'); // 可执行
b.length = 10; // 可执行
b = ['aaa'] // 报错
// 如果想将对象冻结,需使用 Object.freeze() 方法 freeze:冻结
var c = Object.freeze({});
// 常规模式下,下面这行代码不起作用
// 严格模式下会报错
c.prop = 123;
6 顶层对象
在 ES5 中 浏览器的顶层对象就是 window ,顶层对象的属性与全局变量时等价的
Node 里面,是 global
在 ES6 中 ,var 和 function声明的变量依旧是顶层对象的属性,但 let const class 声明的全局变量不属于顶层对象的属性
二:变量的解构赋值
1 数组的解构赋值
解构:ES6中允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这称之为解构
数组的解构赋值是按照元素的顺序依次赋值
let [a,b,c] = [1,2,3]
// a:1 b:2 c:3
let [,,a] = [1,2,3]
// a:3
let [a,b] = [1,2,3]
// a:1 b:2
let [a,...b] = [1,2,3,4]
// a:1 b:[2,3,4]
// ... 扩展运算符,将一个数组变成参数序列
let [a,b,...c] = [1]
// a:1 b:undefined c:[]
let [a] = [];
// a:undefined
let [a] = 1;
// 报错
// 默认值
//**注意:当一个数组成员严格等于( === )undefined时,默认值才会生效**
let [x=1] = [undefined];
// x:1
let [x=1] = ['undefined'];
// x:'undefined'
let [x=1] = [null];
// x:null;
let [x=1] = [2];
// x:2
2 对象的解构赋值
变量与对象的解构赋值是变量与属性同名,再进行赋值 例:let {a} = {a:1}
对象与对象的解构赋值是先找到同名的属性,再赋值给对应的变量 例:let {a:b} = {a:1} b=>1
let {x,y}={x:1,y:2};
// x:1 y:2;
// 当变量名与属性名不一致时
let a = {x:1,y:2};
let {x:a,y:b}=a;
// a:1 b:2
let {a:b} = {a:2};
// b:2
// a:报错,not undefined
// 对象与对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者
// 例:
const node = {loc: {start: {line: 1,column: 5}}};
let { loc, loc: { start }, loc: { start: { line }} } = node;
console.log(loc); {start:{line:1,column:5}}
console.log(start); {line:1,column:5}
console.log(line); 1
let { loc, loc: { start }, loc: { start: { line }} } =
{loc: {start: {line: 1,column: 5}}};
// 例2
let obj = {p: ['Hello',{ y: 'World' }]};
let { p, p: [x, { y }] } = obj;
console.log(p); ['Hello',{ y: 'World' }]
console.log(x); 'Hello'
console.log(y); 'World'
// 例3
let obj = {};
let arr = [];
({ foo: obj.prop, bar: arr[0] } = { foo: 123, bar: true });
console.log(obj); {prop:123}
console.log(arr); [true]
不加一对小括号时,js在行首遇到{}会解析成代码块,从而引发语法错误
// 有默认值时
//**注意:当一个数组成员严格等于( === )undefined时,默认值才会生效**
let {a,b=1}={a:1};
// a:1 b:1
let {x: y = 3} = {};
// y:3 x:报错,not undefined
let {x,y = 3} = {};
// y:3 x:undefined
let {foo: {bar}} = {baz: 'baz'};
// 报错,因为foo是undefined,所以它的子对象就会报错
// 数组的解构赋值
let arr = [1, 2, 3];
let {0 : first, [arr.length - 1] : last} = arr;
// 由于数组的本质是一个特殊的对象,所以 arr=>{0:1,1:2,2:3} ?
console.log(first);
console.log(last);
3 字符串的解构赋值
const [a,b,c,d,e] = 'hello';
// a:'h'
// b:'e'
// c:'l'
// d:'l'
// e:'o'
let {length:len} = 'hello'
// len:5
// 字符串是一个类数组,所以具有 length 属性
4 数值和布尔值的解构赋值
解构赋值的规则:只要等号右边的值不是对象或者数组,就先将其转换成对象,在进行赋值
let {toString:s} = 123;
s === Number.prototype.toString // true
let {toString:s} = true
s === Boolean.prototype.toString // true
// 因为 数值和布尔 的包装对象都具有 toString 属性,所以 变量s 都会被赋值
let {toString:x} = undefined ; // 报错:TypeError
let {toString:x} = null ; // 报错:TypeError
5 函数的解构赋值
function xy([a,b]){
return a+b;
}
xy([1,2]) ===> 3
let arr = [[1,2],[3,4]].map(([a,b])=>a+b);
// .map()方法:数组中的每一个元素执行map方法中的函数或者方法,返回一个新的数组
// arr ===> [3,7]
// 函数参数也可以使用默认值
// 变量默认值
function xy({x=0,y=0}={}){
return x+y;
}
xy({x:1,y:2}) ===> 3
xy({x:1,a:2}) ===> 1
xy({}) ===> 0
xy() ===> 0
// 参数默认值
function move({x,y}={x:0,y:0}){
return x+y;
}
move({x:1,y:2}) ===> 3
move({x:1}) ===> NaN 因为 y的值是undefined,所以最终结果为NaN
move({}) ===> NaN
// 注意:上述是为参数指定默认值,而不是变量
6 用途
// (1). 交换变量的值
let x = 1;
let y = 2;
[x,y] = [y,x];
// (2). 函数返回多个值
function xy(){
return [1,2,3,4];
}
let [a,b,c,d] = xy();
// a:1 b:2 c:3 d:4
// (3). 函数参数的定义
function xy([x,y,z]){ ... }
xy([1,2,3])
function xy({x,y,z}){ ... }
xy({z:1 , y:2 , x:3})
// (4). 提取 JSON 数据
let jsonData = {
id:1,
status:'OK',
data:[111,222]
}
let {id,status,data:arr} = jsonData;
// id:1 status:'OK arr:[111,222]
// (5). 函数参数的默认值
let ajax = function(url,{
async = true, // async 异步
beforeSend = function(){},
cache = true, // 隐藏
complete = function(){}, // complete 完整的,完成
crossDomain = false, // cross 交叉 domain 领域
global = true, // 全球的,总体的
} = {} ){ ... }
// (6). 遍历map解构
const map = new Map([
[{a:1},1]; // 键是对象
['a',1] // 键是字符串
]);
/* Map()构造函数,是一个键值对的集合,其中键可以时任意类型
集合是一个数组,其中数组中的每一个数组项都是Map实例的键和值*/
map.set('first','hello'); // 添加一组键值对
map.get('first') // 获取指定的属性值
map.has('first') // 判断是否存在某个属性,返回bool
map.delete('first') // 删除指定的属性,返回bool
map.size; // 获取数组的长度
map.clear() // 清除所有
// map 的遍历方法--返回一个迭代器,类似一个对象?
.keys() // 返回键名的遍历器
.values() // 返回键值的遍历器
.entries()// 返回所有成员(键值对)的遍历器
.forEach()// 遍历Map的所有成员
// 例:
for(let val of map.kes()){
console.lof(val);
}
for(let [key,value] of map.entries()){
console.lof(key+"==="+value);
}
// 只想获得键名
for(let [key] of map){
console.log(key);
}
// 只想获得值
for(let [,val] of map){
console.log(value);
}
// Set() 构造函数
三:字符串的扩展
1. 字符的 Unicode 表示法
// 可以以 \uxxxx 表示一个字符,其中 xxxx 时 Unicode码点
'\u{7A}' === 'z'
2. codePointAt()
// 对于字节超出 Unicode 码点的字符,charAt()方法无法读取
// 所以提供了 codePointAt()方法来读取码点
3. String.fromCodePoint()
// 通过码点获取字符,可以识别编号大于 0xFFFF 的字符编码
String.fromCodePoint(0x20BB7);
4. 字符串的遍历器接口
// 使用 for of 遍历字符串,可以识别大于 0xFFFF 码点的字符
for(let code of 'foo'){
console.log(code); // 'f' 'o' 'o';
}
5. normalize()
/* 用于他国语言的语调符号和重音符号 Ǒ(\u01D1)和 O(\u004F)和ˇ(\u030C)合成Ǒ(\u004F\u030C)
两种合成符的判断,js中直接判断为false ,加上 .noemalize()方法后为true */
6. 判断字符串是否存在
let s = "Hello World";
s.startsWith("Hello") // true 判断参数是否在字符串的开头
s.endWith("World") // true 判断参数是否在字符串的结尾
s.includes('o') // true 判断是否存在该字符串
s.startWith("World",6) // true 从第6个位置到结束是否是以"World"开头
s.includes("Hello",6) // false 从第6个位置到结束中是否包含指定字符串
// 注意--------------
s.endWith("Hello",5) // 前5个字符中是否包含指定字符串
7. repeat()
// 返回一个新字符串,表示将指定字符串复制n次
let str = "a".repeat(2); // "aa";
let str = "xy".repeat(2); // "xyxy";
// 参数为小数则会取整,负数则报错,0则是一个空字符串
let str = "xy".repeat(2.9); //"xyxy"
let str = "xy".repeat(-1); // 报错
let str = "xy".repeat(0); // 空字符串
// 如果参数是 0~-1之间的小数,则会先取整,也就是按 0 计算
// 如果参数是 NaN 则会按 0 进行计算
// 如果参数是字符串,则先会用 Number() 进行转换再进行计算
8. padStart() padEnd()
// 字符串填充
.padStart() // 头部补全
.padEnd() // 尾部补全
'xy'.padStart(3,'0'); // "0xy"
'xy'.padEnd(4,'0'); // "xy00"
'xy'.padStart(5,'ab'); // "abaxy"
'xy'.padEnd(5,'ab'); // "xyaba"
// 第二个参数省略,默认用空格补全
'xy'.padEnd(5); // "xy "
9. matchAll()
// 返回一个正则表达式在当前字符串的所有匹配
10. 模板字符串
let str = `<div>${a+1}</div>`;
let str = `<div>${fn()}</div>`;
11. at()
// 返回指定索引对应的字符
let str = "123456"
at(0) // 1
四 数值的扩展
1. Number.isInteger()
// 判断一个数值是否为整数
Number.isInteger(25); // true
Number.isInteger(25.5); // false
2. Number.EPSILON
/* 能够接受的误差范围
Number.EPAILON 是一个常量,表示1与大于1的最小浮点数之差,即 1 与 1.000...0001(51个零)
等同于 Math.pow(2,-52)
常用于浮点数计算后的误差判断,如果小于这个值,则表示误差没有意义,即:不存在误差*/
function withinErrorMargin (left, right) {
return Math.abs(left - right) < Number.EPSILON * Math.pow(2, 2);
}
3. Number.isSafeInteger()
// 判断整数是不是在 -2^53 到 2^53 之间,因为在js内超过这个数值,判断就会出错
Number.isSafeInteger(num) // 返回一个布尔值,注意,只判断数字,其余都为 false
4. Math静态方法的扩展
// 1. Math.trunc()
// 用于去除一个数的小数部分,返回整数部分
Math.trunc(1.222) // 1
Math.trunc(-0.111) // 0
// 对于非数字,会先用 Number() 进行内部转换
Math.trunc(true) // 1
Math.trunc(false) // 0
// 转换失败则返回 NaN
Math.trunc(NaN) // NaN
Math.trunc('foo') // NaN
Math.trunc() // NaN
Math.trunc(undefined) // NaN
// 2. Math.sign()
// 判断一个数是 正数 负数 零 ,如果是非数值,则会先进行 Number() 转换
// 返回 1:正数 -1:负数 0:零 -0:(输入-0的时候) NaN:转换失败 报错:参数语法错误时
Math.sign(55) // 1
Math.sign(-1546) // -1
Math.sign(0) // 0
Math.sign(-0) // -0
Math.sign('ss') // NaN
Math.sign(2aa) // 报错
// 3. Math.cbrt()
// 用于计算一个数的立方根 , 如果是非数值,则会先进行 Number() 转换
// 返回: 计算后的值 NaN
Math.cbrt(8) // 2
Math.cbrt('8') // 2
Math.cbrt('ss') // NaN
// 4. Math.hypot()
// 返回所有参数的平方和的平方根
// 如果参数不是数值,则会先用 Number() 进行转换,只要有一个为NaN,则返回 NaN
// 可以写多个参数
Math.hypot(3,4) // 5
Math.hypot() // 0
Math.hypot(NaN) // NaN
Math.hypot(3,4,'a') // NaN
5. ** 指数运算符
2 ** 2 // 4
2 ** 3 // 8
2 ** 4 // 16
// 计算方式是右结合,即从右往左计算
2 ** 3 ** 2 ==> 2 ** (3 ** 2) // 512
// 可以与 = 结合
let a = 2;
a **= 3 // 8
五:函数的扩展
1 参数默认值
function abc(a=1, b=2){
ruturn a+b;
}
// 注意:使用参数默认值时,函数不能有同名的形参
// 参数也可以是一个表达式
let i = 100;
function abs(y = i + 1){
console.log(y);
}
abs(); // 101
// 与解构赋值一起使用
function foo({x,y = 5} = {}){
console.log(x,y);
}
foo({x:1, y:2}); // 1,2
foo(); // undefined 5
// 注意,如果不设置默认值是 foo()调用会出错,因为只有foo的参数是一个对象时,x,y才会通过结构赋值生成,若果不生成就会报错
// 当知道了默认值后,函数length属性的计算将去掉该参数
// 函数.length : 该函数预定义传入参数的个数
(function(a, b = 1){}).length // 1
// 作用域
// 当设置的参数的默认值时,函数声明初始化时会形成一个单独的 参数 作用域,当初始化结束,该作用域消失
// 如果没有设置默认值,则不存在该作用域
function abc(x, y = x){ return y;}
// 当abs进行初始化时,参数会形成一个单独的作用域,此时 y 的默认值 x 指向 第一个参数 x
// 所以 abc(90) 会返回 90
let x = 1;
function foo(y = x){
let x = 2;
console.log(y);
}
// 当foo进行初始化时,参数会形成一个单独的作用域,由于作用域链的问题,所以 x 会像上查找,
// 查找不到则会报错,即:此时初始化结束后就相当于 y = 1 ;
2. rest参数
ES6中引入了rest参数(形式为 …变量名),用于获取函数的多余参数,获取后的格式为一个数组
function foo(x, ...values){
let sum = 0 + x;
for(let val of values){
sum += val;
}
return sum;
}
foo(1,2,3,4,5,6) 21
// 函数的 length 属性不包括 rest 参数
3. name属性
// 返回该函数的函数名
function foo(){};
foo.name; // 'foo'
var a = function(){};
a.name; // ES5总为 '' ES6中为 'a'
4. 箭头函数
注意:使用了箭头函数后,这个函数里面就没有自己的this,里面所出现的this是外部函数的this,而不是指向的this,这种情况也称为this指向的固定化
// 只有一个参数
let f = v => v;
// 等同于
let f = function (v) {
return v;
}
// 无参数
let f = () => 5;
// 等同于
let f = function () {
return 5;
}
// 多个参数
let sum = (num1,num2) => num1 + num2;
// 等同于
let sum = function(num1, num2){
return num1 + num2;
}
// 返回一个对象
// 由于代码块是用 {} 包含起来的,所以返回一个对象时需用 () 包含起来,否则对象的 {} 会被解析成函数体
let a = id => ({id:id,num:1})
// 与解构赋值一起使用
let a = ({first,last}) => first + '' + last;
// 与 rest 参数一起使用
let a = (...arr) => arr;
5. 双冒号运算符
改变this指向,即:双冒号左边的对象是右边函数中的this
// 1. obj::foo
等同于 foo.bind(obj)
6. 尾调用优化
尾调用:指某个函数的最后一步调用了一个函数,即:return y(x); 的严格格式
尾调用优化:每一次函数调用都会在内存中形成一个"调用记录",又称“调用帧”,保存调用位置和内部变量等信息,所有的调用帧称之为 调用栈 ,如果内部函数用到外部函数的变量,就不会进行尾调用优化
尾递归:函数调用自身,称之为递归,尾调用自身,称之为尾递归
六:数组的扩展
1. 扩展运算符
// ... ,将一个数组转为用逗号分割的参数列表,内部使用for...of循环
console.log(...[1,2,3]) // 1 2 3
// 一般用于函数调用
let arr = [1,2,3,4,5,6];
foo(...arr)
// 数组最大值判断
let arr = [1,2,3,4,5,6];
Math.max(...arr);
// 数组中的每一项添加到另一个数组中
let arr = [1,2,3,4];
let brr = [5,6,7,8];
arr.push(...brr);
// 数组复制--浅拷贝
let arr = [1,2,3];
let brr = [];
brr = [...arr];
// 数组合并--浅拷贝
let arr = [1,2,3,4];
let brr = [5,6,7,8];
let crr = [...arr , ...brr];
// 数组与结构赋值一起使用
const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest // [2, 3, 4, 5]
// 字符串转数组
let arr = [...'hello']
// ['h','e','l','l','o']
// 类数组转换成规定的数组
let liArr = document.querySelectorAll('li');
let arr = [...liArr];
/* query.selectorAll 方法返回的是一个NodeList对象。不是一个数组,而是一个类似数组的对象,
之所以可以用数组的方法,是因为nodeList对象实现了Iterator(迭代)接口*/
2. Array.from()
将两类对象转换成规定的数组:类似数组的对象(具有length属性),可遍历迭代对象
如果参数是一个规定的数组,该方法会返回一个一模一样的新数组------即:复制
let arrayLike = {
'0':'a',
'1':'b',
'2':'c',
length:3
}
let arr = Array.from(arrayLike);
// ['a','b','c','d']
Array.from()方法还有第二个参数,作用类似于map()方法,将数组中的被一个元素进行处理并返回
Array.from(arr,x=>x*x)
// 等同于
Array.from(arr).map(x=>x*x);
// 例:取出每个节点的内容,并形成一个数组
let liObj = document.querySelectorAll('li');
let arr = Array.from(li,v=>v.innerHTML);
// 例:将布尔值为false的数组项转换成0
let arr = [1,2,,3,,5,,0,];
let brr = Array.from(arr,v=>v||0);
// [1, 2, 0, 3, 0, 5, 0, 0]
3. Array.of()
用于将一组值转换成数组
Array.of(1,2,3);
// [1,2,3]
4. entries() , keys() , values()
var arr = ['a','b','c', 1,2,3]
arr.keys(); // 返回一个对键名的遍历器---- 一个对象
arr.values(); // 返回一个对键值的遍历器---- 一个对象
arr.entries(); // 返回一个对键值队的遍历器---- 一个对象
// 遍历器中有一个next()方法,里面存放着一条数据,可循环调用来一次输出结果
arr.keys().next(); === {value: 0, done: false}
arr.values().next(); === {value: "a", done: false}
arr.entries().next(); === {value: Array(2), done: false}
arr.entries().next().value; === [ 0,'a']
// 例:用 for of 来循环遍历
for(var [key,value] of arr.entries()){
console.log(key+"="+value);
}
// 0='a' 1='b' 2='c' 3=1 4=2 5=3
5. flat()
// 将多维数组“拉平”
[1,[2]].flat();
// [1,1]
[1,[2,[3]]].flat();
// [1,2,[3]] 默认拉平一层,从外向内拉平
// flat(number);
number:设置拉平的层数
Infinity 关键字,表示无限
// 如果数组有空位,flat()方法会跳过空位
[1,2,3,,4].flat();
// [1,2,3,4]
6. flatMap()
// 对原数组的每一个成员执行一个函数(相当于执行了map()方法),返回一个新数组
// 执行完后再执行flat()方法
[1,2,3,4,5,6].flatMap(x=>x*x); //[1,4,9,16,25,36]
[1,2,3,4].flatMao(x=>[x,x*x]); //[1,1,2,4,3,9,4,16]
7. 数组的空位
/* ES6中 Array.from(),(...),entries(),keys(),values(),find(),findIndex()
等会将空位处理成 undefined */
[...[,1]] // [undefined,1]
七. 对象的扩展
1. 属性简洁写法
// ES6 允许直接在对象中直接写入变量,此时,变量名为属性名,变量值为属性值
let aaa = 1;
let obj = {aaa}; // {'aaa':1}
2. 属性名表达式
let aaa = 'a'
let obj = {
['a'+'bc']:1,
[aaa]:2
}
obj.abc // 1
obj.a // 2
obj.aaa // undefined
obj[aaa]// 2
3. 可枚举性
// 对象中的每个属性的都有一个描述对象(Descriptor),用来控制该属性的行为
Object.getOwnPropertyDescriptor() 方法可以获取该属性的描述对象
let obj = {foo:123};
Object.getOwnPropertyDescriptor(obj,'foo'); // 返回一个对象
// {
// value: 123,
// writable: true,
// enumerable: true, //可枚举性
// configurable: true
// }
// 因为for in 遍历会返回继承的属性,所以可以通过设置 enumerable 属性值为 false 来让其忽略该属性
Object.getOwnPropertyDescriptor(Object.prototype, 'toString')
// ES7 引入了Object.getOwnPropertyDescriptors方法,
// 返回指定对象所有自身属性(非继承属性)的描述对象
4. 属性的遍历
(1) for...in
循环遍历对象自身的和继承的可枚举属性(不包含Symbol属性)
(2) Object.keys(obj)
返回一个数组,包含对象自身的所有属性的(不含继承)所有可枚举属性(不含Symbol属性)的键名
(3) Object.getOwnPropertyNames(obj)
返回一个数组,包含对象自身的所有属性(不含Symbol属性,但是包括不可枚举属性)的键名
(4) Object.getOwnPropertySymbols(obj)
返回一个数组,包含对象自身的所有键名,不管键名时Symbol 或字符串,也不管是否可枚举
(5) Reflect.ownKeys(obj)
返回一个数组,包含对象自身的所有键名,不管键名时Symbol或者字符串,也不管是否可枚举
// 以上 5 种遍历对象的键名都遵守同样的属性遍历的次序规则
- 首先遍历所有的数值键,按照数值升序排序
- 其次遍历所有的字符串键,按照加入时间升序排列
- 最后遍历所有订单 Symbol 键,按加入时间升序排序
// 例:
Reflect.ownKeys({ [Symbol()]:0, b:0, 10:0, 2:0, a:0 })
// ['2','10','b','a',Symbol()]
5. super 关键字
表示指向当前对象的原型对象
let obj = {
foo:123,
foo(){
return super.name;
}
};
Object.prototype.name="ybm";
console.log(obj.foo());
// 注意,此关键词只能用于对象的方法中,在其他地方使用会报错
let obj = {
foo:super.name
}
// 报错,该关键词用于对象的属性中
let obj = {
foo:()=>super.name
}
let obj = {
foo:function (){
return super.name;
}
}
// 报错,因为在JS引擎的解析中,此时的super用在一个函数中,然后赋值给foo属性。
// 所以,目前只有对象方法的简写形式可以让js引擎确认定义的是对象的方法
/*
在JS引擎的内部,super.foo等同于
Object.getPrototypeOf(this).foo 属性
或 Object.getPrototypeOf(this).foo.call(this) 方法
*/
八. 对象的新增方法
1. Object.is()
// 比较两个值是否相等 可比较特殊的字符
Object.is(NaN,NaN) // true
Object.is(+0,-0) // false
Object.is({},{}) // false
Object.is(+0,0) // true
Object.is(-0,0) // false
2. Object.assign()
// 用于的对象的合并,将源对象的所有可枚举的属性复制到目标对象中,不拷贝继承迭代属性
// 若属性名相同,则后面对象的属性值会覆盖前面的属性值
Object.assign(目标对象,源对象)
const target = {a:1};
const target1 = {b:2};
const target2 = {c:3};
const target3 = {a:4};
Object.assign(target,target1,target2,target3)
// target {a:4,b:2,c:3};
// 第二个参数可以为一个字符串,如果是字符串,就会以数组的形式拷贝进目标的对象
// 数值和布尔类型则不会起作用
let str = "abc";
let obj = {a:1};
Object.assign(obj,str);
// obj {'0':'a', '1':'b', '2':'c', a:1}
// 1. 为对象添加方法
Object.assign(SomeClass.prototype,{
someMethod(a,b){
},
anotherMethod(){
}
})
// 等同于下面的写法
SomeClass.prototype.someMethod = function(a,b){};
someClass.prototype.anotherMethod = function(){};
// 2. 拷贝对象
function clone(obj){
return Object.assign({},obj);
// 此方法只能拷贝自身的值,不能拷贝继承的值
}
// 3. 合并多个对象
const merge = (target,...sources) => Object.assign(target,...sources);
3. Object.getOwnPropertyDescriptors()
// 返回指定对象所有自身属性(非继承属性)的描述对象
const obj = {
foo:123,
get bar(){return 'abc'}
};
Object.getOwnPropertyDescriptors(obj);
// { foo:
// { value: 123,
// writable: true,
// enumerable: true,
// configurable: true },
// bar:
// { get: [Function: get bar],
// set: undefined,
// enumerable: true,
// configurable: true } }
4. Object.create()
// 创建一个新对象,并指定新对象的 __proto__ 指向
// 语法 Object.create(proto); 第一个参数为新对象的原型指向
let obj1 = {a:1};
let obj2 = Object.create(obj);
obj2.a; // 1
5. Object.setPrototypeOf()
// 设置指定对象的原型对象,并返回当前对象
// 语法:Object.setPrototypeOf(object,prototype);
let obj1 = {a:1};
let obj2 = {b:2};
Object.setPrototypeOf(obj1, obj2);
obj1.b; // 2
6. Object.getPrototypeOf()
// 返回指定对象的原型对象
let obj = Object,getPrototypeOf(obj1);
obj {b:2};
7. keys() values() entries()
// Object.keys(obj)
// 返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键名
var obj = {foo:'bar',baz:44};
var arr = Object.kes(obj);
arr ['foo','baz'];
// Object.values()
// 返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的属性值
var obj = {a:1,b:2};
var arr = Object.values(obj);
arr [1,2];
// Object.entries() 不支持IE
// 返回一个二维数组,每一个数组项是参数对象自身的(不含继承的)所有可遍历属性
// 的键值对形成的数组,忽略 Symbol
var obj = {a:1,b:2};
var arr = Object.entries(obj);
arr [['a',1],['b',2]]
8. Object.fromEntries()
// Object.fromEntries() 方法是 Object.entries() 的逆操作,
// 用于将一个键值对数组转换成一个键值对形式的对象
// 注:只兼容火狐浏览器
Object.fromEntries([
['foo', 'bar'],
['baz', 42]
])
// { foo: "bar", baz: 42 }
九. Symbol
/*
1. Symbol 值通过 Symbol()函数生成
2. Symbol 函数可以接受一个字符串作为参数,表示对Symbol实例的描述
3. Symbol 可以显式转换成字符串,布尔值,但不能转换成数字
*/
let si = Symbol('foo')
console.log(si)// Symbol(foo)
/*用法?*/
let abc = Symbol('abc');
let obj = {
[abc]:'456',
'abc':'457',
}
十 Set和Map 构造函数
1. Set构造函数
new Set() 生成的是Set的数据结构,其结构内不会有重复的值,是一个类数组对象
内部判断两个值是否相同使用的算法叫做“Same-value-zero equality”,类似于 === 比较,但是该算法中 NaN与NaN相等
let arr = [1,1,2,2,3,3]
const a = new Set();
arr.forEach(x=>a.add(x));
for(let i of a){
console.log(i)
}//1 2 3 4
//1. Set函数可以接受一个数组或具有iterable接口的其他数据结构作为参数
例一:数组为参数
const set = new Set([1,2,3,4,5,6]);
[...set] // [1,2,3,4,5,6]
例二:具有iterable接口的伪数组作为参数
const set = new Set(document.querySelectorAll('div'));
set.size; // 110
//2. 数组去重
var arr = [1,2,3,1,1,2,3,4];
[...new Set(arr)]; // [1,2,3,4]
//3. 字符串去重
var str = '123132451234561321';
[...new Set(str)].join(''); // "123456"
//4. NaN
var [a,b] = [NaN,NaN];
let set = new Set();
set.add(a);set.add(b);
[...set]; // [NaN]
//5. 并集,交集,差集
let a = new Set([1,2,3]);
let b = new Set([2,3,4]);
// 并集
[...new Set(...a,...b)]; //[1,2,3,4]
// 交集
[...new Set([...a].filter(x=>b.has(x)))]; //[2,3]
// 差集
[...new Set([...a].filter(x=>!b.has(x)))]; //[1]
// Set 结构的实例属性
Set.prototype.constructor :构造函数,默认就是Set函数
Set.prototype.size :返回 Set 实例的成员总数
// Set 结构的实例方法
// 1> 操作方法
.add(value) :添加某个值,返回Set结构本身
.delete(value) :删除某个值,返回一个布尔值,表示删除是否成功
.has(value) :判断是否存在这个值,返回一个布尔值
.clear() :清除所有的成员,无返回值
// 2> 遍历方法、
.keys() :返回键名的遍历器
.value() :返回键值的遍历器
.entries() :返回键值对的遍历器
.forEach() ;使用回调函数遍历每个成员
// 由于 Set 结构没有键名(或者说键名和键值是一样的),所以keys() 和 values() 方法得到的结果一样
var arr = [1,2,3];
let set = new Set();
for (let item of set.entries()) {
console.log(item); // [1,1] [2,2] [3,3]
}
// Set 结构的实例默认是可遍历的,默认遍历器生成函数就是它的values方法
// 所以可以直接省略values方法,直接用 for...of遍历
for (let item of set) {
console.log(item); // 1 2 3
}
2. WeakSet结构
1. 与Set类似,也是不重复的值的集合,但是WeakSet的成员只能是对象
2. WeakSet为弱引用,即:垃圾回收机制不计算WeakSet对该对象的引用次数。所以,WeakSet适合存储一些临时数组,当该数组在外部引用此时为0时,他在WeakSet中的引用就会自动消失
3. 由于成员是弱引用,随时会消失,所以ES6规定WeakSet不可遍历
4. 该构造函数可以接受一个数组,或者一个类数组(具有 Iterable 接口)对象,该数组的成员会自动转换成WeakSet实例对象的成员
// WeakSet结构的三个方法
-WeakSet.prototype.add(value);
-WeakSet.prototype.delete(value);
-WeakSet.prototype.has(value);
let arr = [[1,2],[2,3]];
var weak = new WeakSet(arr);
// {[1,2],[2,3]}
3. Map数据结构
1. js对象本质上是键值对的集合,但是传统上只能用字符串作为键,所以限制性大,因此ES6提供了一种新的数据结构,类似对象,也是一个键值对的集合,但键名可以是任意类型
// 实例的属性和操作方法
.size //放回Map结构的成员总数
.set(key,value) //添加或者更新一组键值对,返回整个Map数据结构
.get(key) //返回指定键的值,若不存在,则返回undefined
.has(key) //判断是否存在指定键,返回布尔值
.delete(key) //删除指定键
.clear(key) //删除所有的键,无返回值
// 遍历方法---遍历顺序就是插入顺序
keys():返回键名的遍历器。
values():返回键值的遍历器。
entries():返回所有成员的遍历器。
forEach():遍历 Map 的所有成员。
// 默认的遍历器接口为entries()
// 1. 基本使用方法
let map = new Map();
let m = {a:'1'};
map.set(m, 'content');
map.get(m); // content;
map.has(m); // true;
map.delete(m); // true;
map.has(m); // false
// 2. 可以接受一个双成员的二维数组,或者具有Iterable接口,且每个成员都是一个双元素的类数组对象作为参数
let map2 = new Map([['a',1],['b',10]]);
map2.get('a'); // 1
map2.get('b'); // 10
// 使用Set 和 Map 实例作为参数
const set = new Set([
['foo', 1],
['bar', 2]
]);
const m1 = new Map(set);
m1.get('foo') // 1
const m2 = new Map([['baz', 3]]);
const m3 = new Map(m2);
m3.get('baz') // 3
// 3. 读取一个未知键,则返回undefined
new Map().get('a'); // undefined
// 4. 在Map中 NaN与NaN相等
let mp = new Map();
mp.set(NaN,'1');
mp.set(NaN,'2');
mp.get(NaN); // 2
4. WeakMap
WeakMap结构与Map结构相同,也是用于生成键值对的集合
1. 只接受键名作为键名,2. 键名所对应的值不计入垃圾回收机制
// 一般用于对象在将来会消失,这样这个对象所对应的键值也会被回收,不需要手动回收
let myElement = document.getElementById('logo');
let myWeakmap = new WeakMap();
myWeakmap.set(myElement, {timesClicked: 0});
myElement.addEventListener('click', function() {
let logoData = myWeakmap.get(myElement);
logoData.timesClicked++;
}, false);
// 当DOM元素被删除时,WeakMap中该对象所对应的键值对也会自动删除
// 操作方法
.set()
.get()
.has()
.delete()
十一:Promise
一种异步解决的方案,一个容器,保存着未来某个时间段某个异步操作的结果
特点:
1. promise对象的状态不受外界所改变,它代表着一种异步操作,有三种状态:pending(进行中),fulfilled(成功),rejected(失败),只有结果才能获取是哪一种状态
2. 状态一旦改变一次,就不可再次更改,状态改变只有两种:pending–fulfilled,pending–rejected。只要这两种状况发生,状态就定型了,这是就称为resolved(已定型)
缺点
1. 无法取消,一旦新建就会立即执行,无法中途取消
2. 如果不设置回调函数,promise内部出现的错误不会反应到外部
3. 当处于pending状态时,不知道目标是处于那一阶段(刚开始or已完成)
// 基本使用:
const promise = new Promise(function(resolve, reject){
// 异步代码...
if('异步成功时'){
resolve();//成功的回调函数调用
}else{
reject();//失败的回调函数调用
}
})
promise.then(callback1,callback2);
//.then()方法用于绑定该对象结果的两个阶段所对应的回调函数
//.then()方法会在当前脚本所有同步任务执行完后才会执行
//如果第一个.then()方法中返回的是一个promise对象,
// 那么第二个.then()方法会等待第一个的状态改变时再来判断是否执行
promise.then().catch(callback)
//.catch()方法:当异步操作抛出错误或者回调函数运行出错时会被被该方法捕获
//如果promise的状态已经为成功状态,那么再抛出异常就会无效
.finally()
// 用于指定不管promise对象最后的状态如何,都会执行的操作
promise.then(result => {...})
.catch(error => {...})
.finally(()=>{...})
// 本质是then()方法的特例,等同于
.then(result => {},error => {}) //成功或者失败的方法各写一遍
// 例:请求完后关闭服务器
promise.then().finally(server.stop);
Promise.all()
// 用于将多个Promise实例,包装成一个新的Promise实例,并返回
const p = Promise.all([p1, p2, p3]);
/* 接受一个数组作为参数,数组中的每一个成员都是一个Promise实例
p的状态由 p1,p2,p3决定,
当3个都为fulfilled时,p的状态才为fulfilled,此时p1,p2,p3的返回值会组成一个数组传递给p的回调函数
当其中一个为reject时,p的状态为rejected,此时第一个rejected状态的实例的返回值会传递给p的回调函数
注意:若作为参数的promise实例自己定义了catch方法,那么就不会触发Promise.all()所定义的catch()
*/
Promise.race()
// 用于将多个Promise实例,包装成一个新的Promise实例
const p = Promise.race([p1, p2, p3]);
/* 接受一个数组作为参数,数组中的每一个成员都是一个Promise实例
当参数中某个Promise实例的状态率先发生改变时,p的状态也会发生改变,率先改变的返回值会传递给p的回调函数
*/
// 指定时间内没有获得结构就返回rejected
var p = Promise.race([p1,
new Promise((res,error)=>{
setTimeout(()=>{error();},5000);
})]);
p.then().catch(); // 第一个参数5秒内没有执行成功就抛出失败
Promise.resolve()
将现有对象转换成一个Promise对象
const ps = Promise.resolve(对象)
// 当参数是一个thenable对象,即:具有then()方法的对象时,转换完后会立即执行该方法
// 当参数不是具有then()方法的对象,或根本不是一个对象时,该方法会自动生成一个新的Promise对象,状态为
// 成功状态,并把参数传递给相对应的对调函数
// 当无参数时,该方法会自动创建一个新的Promise对象,状态为成功状态
promise.reject()
返回一个新Promise实例,状态为rejected,并立即执行回调函数
//注:与resolve()不同的是reject()方法会把创建时的参数原封不动的传递给回调函数
promise.try()
为了方便不管是同步还是异步都用promise管理代码
十二:Iterator(遍历器)
是一种接口,为不同的数据结构提供一种统一的访问机制,任何数据只要部署了Iterator接口都可以完成遍历操作
遍历过程:
(1)创建了一个指针对象(即:遍历器对象),指向当前数据结构的起始位置
(2)第一次调用指针对象的next()方法,指针就指向数据结构的第一个成员
(3)第二次调用指针对象的next()方法,指针就指向数据结构的第±q二个成员
(4)依次类推,不断调用指针对象的next()方法,知道它指向数据结构的结束位置
每一次调用next方法,都会返回数据结构的当前成员信息。即:一个包含 value 和 done 两个属性的对象,其中 value 属性时当前成员的值,done
ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator
属性,这个属性是一个函数表达式,执行它就会返回一个遍历器
for…of循环会调用Symbol.iterator属性,返回返回对象中的value值
// 模拟next()方法
function makeIterator(obj){
let nextIndex = 0;
return {
next:()=>{
return nextIndex < obj.length ?
{value:obj[nextIndex++], done:false}:
{value:undefined, done:true}
}
}
}
var arr = [1,2,3];
var obj = makeIterator(arr);
obj.next(); //{value:1}
obj.next(); //{value:2}
obj.next(); //{value:3}
obj.next(); //{done:true}
1.元素js中具有Iterator接口的数据结构:
Array
Map
Set
String
TypedArray
函数的 arguments 对象
NodeList 对象(类似数组的对象)
// 例:调用数组的 Symbol.iterator属性
var arr = [1,2]
var iter = arr[Symbol.iterator]()
// Symnol值要用[]括起来
iter.next(); // {value:1, done:false}
iter.next(); // {value:1, done:false}
iter.next(); // {value:undefined, done:false}
2.调用Iterator接口的场合
1. 解构赋值
var arr = new Set().add(1).add(2).add(3)
var [x,y] = arr // x:1 y:2
var [first,...rest] = arr //first:1 rest:[2,3]
2. 扩展运算符
var arr = "hello"
[...arr] // ['h','l','l','o']
3. 特殊的类数组对象:
例:querySelectorAll()
例:
var arrayLike1 = {
0: 'a',
1: 'b',
length: 2,
[Symbol.iterator]: Array.prototype[Symbol.iterator]
// 需要具有Symbol.iterator属性
};
// 报错
for (let x of arrayLike1) {
console.log(x);
}
3. 任何接收数组作为参数的场合
-for...of
-Array.from()
-Map(),Set(),WeakMap(),WeakSet()
-Promise.all()
-Promise.race()
4.遍历器对象的return(),throw()方法
/* 遍历器除了具有next()方法,还具有return()方法和throw()方法
当for...of循环提前退出(出错时 或 break语句),就会调用 return 方法
注意:return方法必须返回一个对象,这是Generator规格规定
*/
function readLinesSync(file) {
return {
[Symbol.iterator]() {
return {
next() {
return { done: false };
},
return() {
file.close();
return { done: true };
}
};
},
};
}
// 情况一
for (let line of readLinesSync(fileName)) {
console.log(line);
break;
}
/* 当执行break方法后就会执行遍历器的return()方法 */
// 情况二
for (let line of readLinesSync(fileName)) {
console.log(line);
throw new Error();
}
/* 情况二在执行return方法关闭文件之后,再抛出错误 */
十三:Generator 函数
1.函数定义
// 1. function关键字与函数名的中间用 * 号连接
// 2. 函数体内部用 yield 表达式来表示不同的状态
// 3. 调用Generator函数后返回一个遍历器对象,即:指针对象,注:此时Generator函数内部代码还没有执行
// 4. 遍历器对象使用.next()方法进行执行函数体代码,直到遇到 yield 表达式 或 return 语句停止
// 5. 遇到yield表达式时next()方法返回一个对象,{value:当前yield表达式的值, done:布尔值(当前遍历的状态)}
// 6. yield 关键字相当于一个暂停符,.next()方法就为恢复执行
// 7. 返回的遍历器对象不是this对象
// 8. 遍历器对象可以调用函数原型上的方法
function* hello(){
console.log(1)
field 'a'
console.log(2)
field 'b'
console.log(3)
return 'c'+'d'
console.log(4)
}
var hl = hello()
hl.next() // 1 {value:a, done:false}
hl.next() // 2 {value:b, done:false}
hl.next() // 3 {value:cd, done:true} 遇到return时 done的值就为 true
hl.next() // {value:undefined, done:true} // 因为上一句已经return了,所以4不会被输出
2.next()方法的参数
// 1. yield 表达式本身是没有返回值的,或者说总是undefined
// 2. next()方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值
// 3. 因此第一个调用.next()方法里面传参是无效的,V8引擎会直接忽略第一次调用的参数
// 4. 调用函数后需调用一次next()方法来启动Generator函数的内部代码
function* f() {
for(var i = 0; true; i++) {
var reset = yield i;
if(reset) { i = -1; }
}
}
var g = f();
g.next() // { value: 0, done: false }
g.next() // { value: 1, done: false }
g.next() // { value: 2, done: false }
g.next(true) // { value: 0, done: false }
// 前三次执行时,reset为undefined,第四次执行时,next()方法传了一个参数true,
// 所以yield表达式的,返回值就为true,i=1,i++就为0
// 此特性可以在特定阶段从函数外部向内部传入值
function* foo(x) {
var y = 2 * (yield (x + 1));
var z = yield (y / 3);
return (x + y + z);
}
var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }
3.for … of 循环
// for ... of 循环可以自动遍历Generator函数时所生成的iterator对象
// 例:实现斐波那锲数列
function* fbnq(){
let [a,b] = [0,1]
for(;;){
yield b
[a,b] = [b,a+b]
}
}
for(var n of fbnq()){
if(n>1000) break
console.log(n)
}
4. return()方法
// 调用遍历器对象的return()方法后返回对象中的value值就是return()方法的参数,并且done的值为true
// 此时,Generator函数的遍历就会停止,如果无参数,则value值为undefined
function* gen() {
yield 1;
yield 2;
yield 3;
}
var g = gen();
g.next() // { value: 1, done: false }
g.return('foo') // { value: "foo", done: true }
g.next() // { value: undefined, done: true }
5.yield* 表达式
// 用于在一个Generator函数中调用另外一个Generator函数
// yield* 表示返回的是一个遍历器对象,若不加js不会自动识别,会输出这个对象
// 无 return 语句时
function* foo(){
yield 'a'
yield 'b'
}
function* bar(){
yield 'x'
yield* foo()
yield 'y'
}
for(let n of bar()){
console.log(n) // 'x' 'a' 'b' 'y'
}
// 有 return 语句时,可以用一个变量来接收 field* foo 表达式的返回值
function* foo(){
yield 'a'
yield 'b'
return 'c'
}
function* bar(){
yield 'x'
let a = field* foo()
console.log(a)
yield 'y'
}
for(let n of bar()){
console.log(n) // 'x' 'a' 'b' 'c'y'
}
// 例:利用yield* 命令遍历取出二维数组
function* iterTree(tree) {
if (Array.isArray(tree)) {
for(let i=0; i < tree.length; i++) {
yield* iterTree(tree[i]);
}
} else {
yield tree;
}
}
const tree = [ 'a', ['b', 'c'], ['d', 'e'] ];
for(let x of iterTree(tree)) {
console.log(x);
}
or [...iterTree(tree)]
6 作为对象属性的 Generator 函数
let obj = {
* myGeneratorMethod(){} // myGeneratorMethod: function* (){}
}
7 状态判断保存
var clock = function* () {
while (true) {
console.log('Tick!');
yield;
console.log('Tock!');
yield;
}
};
8 Thunk 函数
// 传值调用:参数为表达式时,表达式在进入函数体之前就已经计算完成
// 传名调用:参数为表达式时,表达式只有在函数体中使用到时才进行计算
Thunk 函数的定义:将一个参数放到一个临时函数内,再把临时函数当参数传入函数体中,
这个临时函数就是Thunk函数
十四:async函数
// 1. 语法与 Generator 函数相似
var asyncRead = async function(){
const f1 = await f1()
}
// 1. 返回一个promise对象
// 2. 有两个关键字 async await 分别对应 * yield
// 3. async 函数内置执行器,即:不需要像Generator函数需要.next()方法执行
// 4. await命令后面可以是promise对象和原始数据类型(但是这时会立即自动转换成resolved 的 promise对象)
// 5. async函数内部return 语句返回的值会成为then方法回调函数的参数
// 6. async函数内部抛出错误会直接返回的Promise对象的变成reject状态,然后被catch捕获
// 7. 当遇到第一个await时,后面的代码都会被放入队列中执行
// 8. 只有当await后面的promise对象执行完后,他的状态才会改变
// 9. 只用当async函数内部的所有异步操作执行完后才会执行then方法
// 10 若将异步操作放到try...catch里面,则该异步操作无论是否成功,后面的异步操作都会继续执行
1. 异步函数的触发
// 1. 顺序触发
async function myFunction() {
await getFoo()
await getBar()
} // 只用getFoo()执行完后 getBar()才会执行
// 2. 同时触发
async function myFunction() {
var fooPromise = getFoo()
var barPromise = getBar()
await fooPromise
await barPromise
// 此时就相当于两个异步函数的执行结果被await拦截了
}
async function myFunction() {
var [foo,bar] = await Promise.all([getFoo(),getBar()])
}
十五:Class类
1.定义
// Class 类的基本使用
class Point{
constructor() {
// 实例属性
this.name = 2
}
// 原型对象上的实例方法
toString() {}
// 静态属性
static a = '41'
// 静态方法
static show(){ }
}
var p = new Point()
p.toString()
// 1. 使用方法与构造函数相同
// 2. 方法之间不需要用逗号分隔
// 3. 类中的所有方法实际上是定义在 类名的prototype上
即等同于:Point.prototype = {
constructor(){},
toString(){}
}
所以类的实例调用方法其实就是调用原型上的方法
// 4. 类中的所有的方法都是不可枚举的
Object.keys(Point.prototype) //[]
// 5. 类没有变量提升
// 类名表达式
var cls = class Me{}
var cls = class {}
var cls = new class {} // 这样就可以直接调用class里面的方法了
2. constructor() 方法
// 1. 每个类在创建的时候必须有一个constructor()方法
// 2. 如果没有显示创建,则系统会默认创建一个
// 3. 该方法会在 new 命令后自动调用,默认返回实例对象,即:this,也可以改写此属性,返回另一个对象
class Point {
z = 0;
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ','this.z+')';
}
}
var p = new Point(1,2)
p.toString() // (1,2)
3. get set关键字
// 在类的内部通过 set get 关键字来拦截某个属性的存取值的操作
// 调用时不能加括号,当表达式调用
class MyClass{
set prop(val){
this.val = val
console.log(this.val+'a')
}
get prop(){
console.log(this.val)
}
}
var myclass = new MyClass()
myclass.prop = 1 // 注意,此时没有加小括号 '1a'
myclass.prop 1
4. extends
// 继承
class Bar{}
class aaa extends Bar {
constructor(){
super(); // 该关键词的作用是调用父类的constructor方法,子类中必须写,
// 否则创建实例时报错---重写constructor()时
// 作为对象时代表指向父类的原型对象,在静态方法中,指向父类
// 因为指向的是原型对象,所以父类上实例的属性和方法,super访问不到
// 通过super调用父类上方法中的this代表的是当前子类的实例
}
}
// super()当函数时使用
class A {
constructor(name,age){
this.name = name
this.age = age
}
say(){
console.log(this.name,this.age)
}
}
class B extends A{
constructor(...args){
super(...args) // 分配内存空间,指向this,同时将父类的属性挂载到this上
// super时父类构造器的一个引用,调用super就是调用父类的构造函数
// super前不能使用this
}
}
const b = new B('xy', 18)
// super当对象时使用
class A {
p() {
return 2;
}
}
class B extends A {
constructor() {
super();
console.log(super.p()); // 2
}
}
let b = new B();
5. 属性
// name属性
class Bar{}
Bar.name // 'Bar'
// this
class 中的 this 是指向当前实例
// static
// 当一个方法名前加了static关键词时,该方法就不会被实例所继承,但可以被他的 子类 继承
class Bar{
static add(){this}
// 注意:其中的this是指类,而不是指实例
}
// 静态属性的定义
class Bar{
}
Bar.a = 1 //注意,写在外面
6. Object.getPrototypeOf()
// 该方法可以从子类上获取父类
Object.getPrototypeOf(子类名) === 父类名
// 可以用该方法来判断一个类是否继承了另一个类