1,解构赋值
let [head, ...tail] = [1, 2, 3, 4];
console.log(head) //1
console.log(tail) //[2, 3, 4]
let [x, y, ...z] = ['a'];
console.log(y) //undefined
console.log(z) //[]
let [a, [b], d] = [1, [2, 3], 4];
a // 1
b // 2
d // 4
let [er, tr, gr,yr] = [ 2,2,5, 1].sort();
console.log(tr)//2
let [x1, y1 = 'b'] = ['a', undefined];
console.log(y1);//b
let [x = 1] = [null];
x // null
let { foo: baz,bar:jjjfd } = { foo: 'aaa', bar: 'bbb' };
log(baz);//aaa
log(jjjfd);//bbb
const node = {
loc: {
start: {
line: 1,
column: 5
}
}
};
let { loc, loc: { start }, loc: { start: { line }} } = node;
line // 1
loc // Object {start: Object}
start // Object {line: 1, column: 5}
var {x: y = 3} = {x: 5};
y // 5
let {length : len} = 'hello';
len // 5
let apss = [[1, 2], [3, 4]].map(([a, b]) => a + b);
log(apss);// [3,7] map重新返回新的数组,
字符串的方法 // String.fromCodePoint
dePoint()方法,可以识别大于0xFFFF的字符
// let text = String.fromCodePoint(0x20BB7);
let text = 'fdsa放大发';
// for...of //为遍历字符串的方法
for (let i of text) {
log(i); // 分别输出 fdsa放大发
}
// 单行
// `In JavaScript '\n' is a line-feed.`
// 多行字符串
// `In JavaScript this is
// not legal.`
// 字符串中嵌入变量
let name = "Bob", time = "today";
log(`Hello ${name},
how are you ${time}?`);//Hello Bob,
//how are you today?
// 大括号内部可以放入任意的 JavaScript 表达式,可以进行运算,以及引用对象属性。
let x232 = 1;
let y2323 = 2;
log(`${x232} + ${y2323 * 2} = ${x232 + y2323 * 2}`)//1 + 4 = 5
function fn() {
return "Hello World";
}
log(`foo ${fn()} bar`)//foo Hello World bar
let s = 'Helloworld!';
// log(s.startsWith('Hello'))// true返回布尔值,表示是否找到了参数字符串。
// s.endsWith('!') // true 返回布尔值,表示参数字符串是否在原字符串的头部。
// s.includes('o') // true 返回布尔值,表示参数字符串是否在原字符串的尾部
'hello'.repeat(2) // "hellohello" repeat方法返回一个新字符串,表示将原字符串重复n次
// 如果某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全。
'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'
// ES2021 引入了replaceAll()方法,可以一次性替换所有匹配。目前浏览器不适配 重写replaceAll方法
String.prototype.replaceAll = function(s1,s2){
return this.replace(new RegExp(s1,"gm"),s2);
}
log('aabbcc'.replaceAll('b', '_'));//aa__cc
数值的扩展
// ES5的写法
parseInt('12.34') // 12
parseFloat('123.45#') // 123.45
// ES6的写法
Number.parseInt('12.34') // 12
Number.parseFloat('123.45#') // 123.45
Number.isInteger();//用来判断一个数值是否为整数。
Math.trunc();//方法用于去除一个数的小数部分,返回整数部分。
log(Math.trunc('123.456') );//123
// Math.sign方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。
// 它会返回五种值。
// 参数为正数,返回+1;
// 参数为负数,返回-1;
// 参数为 0,返回0;
// 参数为-0,返回-0;
// 其他值,返回NaN。
Math.sign(-5) // -1
Math.sign(5) // +1
Math.sign(0) // +0
Math.sign(-0) // -0
Math.sign(NaN) // NaN
// 新增了一个指数运算符(**)
// 这个运算符的一个特点是右结合,而不是常见的左结合。多个指数运算符连用时,是从最右边开始计算的。
log(2 ** 3 ** 2)//512
函数的扩展
// 写法一
function m1({x = 0, y = 0} = {}) {
return [x, y];
}
// 写法二
function m2({x, y} = { x: 0, y: 0 }) {
return [x, y];
}
// 函数没有参数的情况
m1() // [0, 0]
m2() // [0, 0]
// x 和 y 都有值的情况
m1({x: 3, y: 8}) // [3, 8]
m2({x: 3, y: 8}) // [3, 8]
// x 有值,y 无值的情况
m1({x: 3}) // [3, 0]
m2({x: 3}) // [3, undefined];//不传y值类似于没有设置y这个变量
// x 和 y 都无值的情况
m1({}) // [0, 0];
m2({}) // [undefined, undefined]
m1({z: 3}) // [0, 0]
m2({z: 3}) // [undefined, undefined]
// 函数的length属性:该函数预期传入的参数个数。某个参数指定默认值以后,预期传入的参数个数就不包括这个参数了。同理,后文的 rest 参数也不会计入length属性。
// 如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了
log((function (ty = 0, bf, cfd) {}).length)
log((function (adf, bdd = 1, crr) {}).length)
// rest参数的写法
// rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
const sortNumbers = (...numbers) => numbers.sort();
// log(sortNumbers(3,5,5,46))
// 数组排序的简单方法
// sort():简单的排序,只判断字符串的第一位数字; sort(value):value可以传true或者false,true在前,false在后
let values = [3,89,30,5];
var result = values.sort((a, b) => a - b);
数组的扩展
// ES5 的写法
// function f(x, y, z) {
// // ...
// }
// var args = [0, 1, 2];
// f.apply(null, args);
// ES6的写法
function f(x, y, z) {
// log(x,y,z)
}
let args = [0, 1, 2];
f(...args);
// ES6 的写法
let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1.push(...arr2);
const a1 = [1, 2];
// 写法一
const a2 = [...a1];
// 写法二
const [...a2] = a1;
// log(a2)
[...'hello']
// [ "h", "e", "l", "l", "o" ]
// Array.from方法用于将两类对象转为真正的数组
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
let arr2989 = Array.from(arrayLike);
// log(arr2989);
Array.from([1, , 2, , 3], (n) => n || 0)
// [1, 0, 2, 0, 3]
// 数组实例的copyWithin()方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组。
// target(必需):从该位置开始替换数据。如果为负值,表示倒数。
// start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示从末尾开始计算。
// end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示从末尾开始计算。
// 这三个参数都应该是数值,如果不是,会自动转为数值
[1, 2, 3, 4, 5].copyWithin(0, 3, 4)
// [4, 2, 3, 4, 5]
[1, 2, 3, 4, 5].copyWithin(0, -2, -1)
// [4, 2, 3, 4, 5]
// 数组实例的 find() 和 findIndex()
// 数组实例的find方法,用于找出第一个符合条件的数组成员
log([1, 4, -5, 10].find((n) => n < 0))
// 上面代码中,find方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组。
// fill方法使用给定值,填充一个数组。
// fill方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置。
log(['a', 'b', 'c'].fill(7, 1, 2))
// ['a', 7, 'c']
***// —entries(),keys()和values()——用于遍历数组 唯一的区别是keys()是对键名的遍历、values()是对键值的遍历***,entries()是对键值对的遍历。
for (let index of ['a', 'b'].keys()) {
// console.log(index);
}
// 0
// 1
for (let elem of ['a', 'b'].values()) {
// console.log(elem);
}
// 'a'
// 'b'
for (let [index, elem] of ['a', 'b'].entries()) {
// console.log(index, elem);
}
// 0 "a"
// 1 "b"
let letter = ['a', 'b', 'c'];
let entries = letter.entries();
// console.log(entries.next().value); // [0, 'a']
// console.log(entries.next().value); // [1, 'b']
// console.log(entries.next().value); // [2, 'c']
// Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似, 该方法的第二个参数表示搜索的起始位置
// log([1, 2, 3].includes(3, 3));//false
// log([1, 2, 3].includes(3, -1));//true
// flat()默认只会“拉平”一层,如果想要“拉平”多层的嵌套数组,可以将flat()方法的参数写成一个整数,表示想要拉平的层数,默认为1。
// log([1, 2, [3, [4, 5]]].flat()); [1, 2, 3, [4, 5]]
// log([1, 2, [3, [4, 5]]].flat(2));// [1, 2, 3, 4, 5]
// flatMap()方法对原数组的每个成员执行一个函数(相当于执行Array.prototype.map()),然后对返回值组成的数组执行flat()方法。该方法返回一个新数组,不改变原数组。
// 相当于 [[2, 4], [3, 6], [4, 8]].flat()
[2, 3, 4].flatMap((x) => [x, x * 2]);// [2, 4, 3, 6, 4, 8]
// 相当于 [[[2]], [[4]], [[6]], [[8]]].flat()
[1, 2, 3, 4].flatMap(x => [[x * 2]]);// [[2], [4], [6], [8]]
const arr = [
'peach',
'straw',
'apple',
'spork'
];
const stableSorting = (s1, s2) => {
if (s1[0] < s2[0]) return -1;
return 1;
};
arr.sort(stableSorting)
// ["apple", "peach", "straw", "spork"]
对象的扩展
// JavaScript 属性名表达式 定义对象的属性,有两种方法。
// obj.foo = true;
// // 方法二
// obj['a' + 'bc'] = 123;
// 表达式还可以用于定义方法名。
let obj = {
['h' + 'ello']() {
return 'hi';
}
};
log(obj.hello()); // hi
// super关键字 我们知道,this关键字总是指向函数所在的当前对象,ES6 又新增了另一个类似的关键字super,指向当前对象的原型对象。
const proto = {
foo: 'hello'
};
const obj323 = {
foo: 'world',
find() {
return super.foo;
}
};
// Object.setPrototypeOf(),为现有对象设置原型,返回一个新对象
// 接收两个参数:第一个是现有对象,第二是原型对象。
// 2、new 命令通过构造函数新建对象实例的过程,其本质是将实例的原型,指向了构造函数的prototype属性,然后在实例上执行构造函数。
Object.setPrototypeOf(obj323, proto);
log(obj323.find()) // "hello"
// // 因此 ES2020 引入了“链判断运算符”(optional chaining operator)?.,简化上面的写法。
// const firstName = message?.body?.user?.firstName || 'default';
// const fooValue = myForm.querySelector('input[name=foo]')?.value
// // 上面代码使用了?.运算符,直接在链式调用的时候判断,左侧的对象是否为null或undefined。如果是的,就不再往下运算,而是返回undefined。
// a?.b
// // 等同于
// a == null ? undefined : a.b
// a?.[x]
// // 等同于
// a == null ? undefined : a[x]
// a?.b()
// // 等同于
// a == null ? undefined : a.b()
// a?.()
// // 等同于
// a == null ? undefined : a()
// ES6 提出“Same-value equality”(同值相等)算法,用来解决这个问题。Object.is就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。
Object.is('foo', 'foo')
// true
Object.is({}, {})
// false
// Object.assign()方法用于对象的合并,将源对象(source)的所有可枚举属性
const target = { a: 1, b: 1 };
const source1 = { b: 2, c: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
log(target) // {a:1, b:2, c:3}
Symbol
let s1 = Symbol()
// 或者,你也可以在调用Symbol()函数时传入一个可选的字符串参数,相当于给你创建的Symbol实例一个描述信息:
let s2 = Symbol('another symbol')
// 由于Symbol是一种基础数据类型,所以当我们使用typeof去检查它的类型的时候,它会返回一个属于自己的类型symbol,而不是什么string、object之类的:
typeof s1 // 'symbol'
// 另外 每个Symbol实例都是唯一的 ,所以当你比较两个Symbol实例的时候,将总会返回false:
const a=Symbol("name");
const b=Symbol("name");
console.log(a===b) //false
const name=Symbol();
const age=Symbol();
let obj={
[name]:"已经代码"
}
obj[age]=18;
console.log(obj[name]);//已经代码
console.log(obj[age]);//18
// 并且 当使用了Symbol作为对象的属性key后不能使用枚举方法
const name=Symbol();
const age=Symbol();
const obj={
[name]:"大师",
[age]:16,
sex:"男"
}
console.log(obj)
console.log(Object.keys(obj));//["sex"] 使用symbol定义的属性,不能使用枚举遍历
for(let p in obj){
console.log(p); // sex
}
// 使用Object的API
console.log(Object.getOwnPropertyNames(obj));// ["sex"] 得到符号类型代替的属性名,并且为数组
console.log(Object.getOwnPropertySymbols(obj));// [Symbol(), Symbol()]
const sybs=Object.getOwnPropertySymbols(obj);
console.log(sybs[0]===name) //true 引用会相等
//补充:
// 1.Object.getOwnPropertyNames()方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。
// 2.Object.getOwnPropertySymbols()方法返回一个给定对象自身的所有 Symbol 属性的数组。
// ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。set可以说是去重的方法
// 例一
const set = new Set([1, 2, 3, 4, 4]);
log([...set]);// [1, 2, 3, 4]
// Array.from方法可以将 Set 结构转为数组。
const items = new Set([1, 2, 3, 4, 5]);
const array = Array.from(items);
function dedupe(array) {
return Array.from(new Set(array));
}
dedupe([1, 1, 2, 3]) // [1, 2, 3]
// Map:Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content')
log(m.get(o));// "content"
m.has(o) // true
m.delete(o) // true
m.has(o) // false
const map = new Map();
const k1 = ['a'];
const k2 = ['a'];
map
.set(k1, 111)
.set(k2, 222);
map.get(k1) // 111
map.get(k2) // 222
// 扩展运算符与其他结构数据转换
// 1:对象转数组 Map 转为数组
const myMap = new Map()
.set(true, 7)
.set({foo: 3}, ['abc']);
[...myMap]
_____________________________________________________________________________________
// ES6 关键字class、constructor、extends、static简介
// 先来看下传统ES5的写法.
// ES5写法
function StdInfo() {
this.name = "cat";
this.age = 30;
}
StdInfo.prototype.getNames = function () {
console.log("name:" + this.name);
}
//得到一个学员信息的对象
var p = new StdInfo()
p.getNames()
// 一、class 使用
// 实质:
// 在类的实例中调用方法,其实就是调用原型上的方法。P = new Parent() P.constructor === Parent.prototype.constructor
// 注意:
// 1、定义类不存在变量提升,只能先定义类后使用,跟函数声明有区别的。
// 2、 定义在类中的方法不需要添加function
// 3、函数的定义方式有函数声明和函数表达式两种,类的定义方式也有两种,分别是:类声明和类表达式。
// 4、表达式申明时候,class关键字之后的类名可有可无,如果存在,则只能在类内部使用
// 5、方法之间不需要加逗号,否则会报错
// 6、类必须使用new调用,否则会报错 (ES5 中可以直接调用)
// 7、动态添加方法
Object.assign(Parent.prototype, {
getName() {
console.log('覆盖原来定义的')
}
})
// 表达式:
const People = class StdInfo {
constructor(){
console.log(StdInfo);
// 这里可以打印出值,是一个函数
}
getName () { // 不需要添加function
}
}
// 声明:
class StdInfo { }
// 二、constructor
// 意义:类初始化时候执行的函数
// 作用:初始化属性, 传入所需要的参数
// 返回值:自动返回实例对象
// 注意点:1、可以省略constructor ,一个类中只能有一个constructor函数,定义多个会报错
// 2、继承时候super() 需要写在constructor里面调用,然后才能使用this
class Parent {
constructor(name, age) {
super(name, age) //初始化属性
this.name = name // super 之前会报错, 应该写在super 之后
}
}
// 三、继承 extends
// 和ES5原理对比:、继承 extends ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this) )。
// ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。
// 注意点:1、如果子类中有constructor构造函数,则必须使用调用super。且把父级的参数继承下来
// 2、子类必须在constructor方法中调用super方法,否则新建实例时会报错(this is not defined)
// 这是因为子类没有自己的this对象,而是继承父类的this对象。如果不调用super方法,子类就得不到this对象。
class Parent {
constructor(name, age) {
this.name = name;
this.age = age;
}
getName() {
console.log(this.name)
}
}
class Child extends Parent {
constructor(name, age) {
super(name, age) //代表父类的构造函数
this.name = name // super 之前会报错, 应该写在super 之后
}
}
// 四、static 的使用
// 解释:类其实就是实例的原型, 所有在类中定义的方法, 都会被实例继承,在new 一个实例后就可以直接使用类中的方法。
// 如果在一个方法前, 加上static关键字,
// 就表示这个方法不会被实例继承, 而是直接通过类来调用,及 “类名”+ ‘.’ + “方法名” , 这就称为“ 静态方法”。
// 作用: 写工具 函数
// 使用
// 1、类中通过类名 直接调用 不能在实例后调用实例的方法。
class StuInfo {
static staticMethodsParent() {
console.log('static 使用1 from 父类');
}
}
StuInfo.staticMethodsParent()
// 2、子类可以调用父类的静态方法
class Parent {
static staticMethodsParent() {
console.log('static 使用1 from 父类');
}
}
class Child extends Parent {
constructor() {
super() // 不管是否有这个super,子类都可以获取到父类的静态方法
}
static staticMethodsChild() {
console.log('static 使用2 from 子类');
}
}
Child.staticMethodsChild() // static 使用2 from 子类'
Child.staticMethodsParent() // static 使用1 from 父类
// 3、 通过关键字this 使用 static 定义的方法
class StuInfo{
constructor () {
this.constructor.getStaticMethodsName()
}
static staticMethodsParent() {
console.log('static 使用3 from 父类');
}
}
StuInfo.staticMethodsParent()
// 4、 通过this 使用static 的第二种情况 ,在一个静态方法中 使用另一个静态方法
class StuInfo {
static staticMethodsParent1() {
return "第一个静态方法返回值"
}
static staticMethodsParent2() {
console.log('第二个静态方法执行得到的结果:' + this.staticMethodsParent1())
}
}
StuInfo.staticMethodsParent2()
// 5、通过super 调用父级的 静态方法
class Parent {
static staticMethodsParent() {
console.log('父级的 static ');
}
}
class Child extends Parent {
constructor () {
super()
}
static staticMethodsChild() {
console.log( super.staticMethodsParent())
}
}
Child.staticMethodsChild()
// prototype 继承
// 所有的 JavaScript 对象都会从一个 prototype(原型对象)中继承属性和方法:
// Date 对象从 Date.prototype 继承。
// Array 对象从 Array.prototype 继承。
// Person 对象从 Person.prototype 继承。
// 所有 JavaScript 中的对象都是位于原型链顶端的 Object 的实例。
// JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
// Date 对象, Array 对象, 以及 Person 对象从 Object.prototype 继承。
// 添加属性和方法
// 有的时候我们想要在所有已经存在的对象添加新的属性或方法。
// 另外,有时候我们想要在对象的构造函数中添加属性或方法。
// 使用 prototype 属性就可以给对象的构造函数添加新的属性:
function Personfds(first, last, age, eyecolor) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eyecolor;
}
// 当然我们也可以使用 prototype 属性就可以给对象的构造函数添加新的方法:
Personfds.prototype.nationality = "English";
function Person(first, last, age, eyecolor) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eyecolor;
}