现在几乎都是使用ES6了,浏览器要兼容的时代随着移动端的统一归于平静,想起以前轰轰烈烈的浏览器大战,为了兼容性痛苦不堪的年代,这一切都过去了。况且随着脚手架等自动化构建工具的使用,直接写最新最酷的代码,再babel一下,放心大胆的用。
所以,我也建议直接用最新版本的语法,不管是语法糖,还是效率,用起来都更舒心。
1、Symbol
ES6为JavaScript引入了一个新的原生类型:Symbol,但是,和其他原生数据类型不一样,symbol没有字面量形式。作为构造函数来说它并不完整,因为它不支持语法:"new Symbol()"。
下面是创建Symbol的过程:
var sym = Symbol('something');
console.log(typeof sym); //symbol
console.dir(Symbol)注意事项:
不能也不应该对Symbol()使用new。它不是一个构造器,也不会创建一个对象。
传给Symbol(...)的参数是可选的,如果传入了的话,应该是一个为这个symbol的用途给出用户友好描述的字符串。
typeof的输出是一个新的值"symbol",这是识别symbol的首选方法。
两个Symbol永远不会相等
console.log(Symbol('name') === Symbol('name')); // false1.1 为对象添加私有成员
利用Symbol不重复的特性,可以为对象添加不重复的键名。
每个从 Symbol() 返回的 symbol 值都是唯一的。一个 symbol 值能作为对象属性的标识符;这是该数据类型的目的。
//使用 Symbol 为对象添加不重复的键
const obj = {}
obj[Symbol()] = '123'
obj[Symbol()] = '456'
console.log(obj)
//也可以在计算属性名中使用
const obj = {
[Symbol()]: 123
}
console.log(obj)利用两个Symbol不相等的特性,可以为对象创建私有成员,外部不能访问。
const axisName = Symbol('obj的别名');
const obj = {
// 利用这种方式可以创建私有成员,外部不能访问
[Symbol('obj的id')]: 1,
[Symbol('obj的name')]: 'obj',
[axisName]: 'myObj',
url: 'http://www.xxx.com'
}
// 外部不能访问,因为没有两个Symbol是一样的。
console.log(obj[Symbol('obj的id')]);
console.log(obj[axisName]);
console.log(obj.url);常规的方式不能获取Symbol属性名
// for...in遍历不出Symbol,仅包含了以字符串为键的属性
for(const key in obj){
console.log(key)
}
// 也不能返回Symbol,仅包含了对象自身的、可枚举的、以字符串为键的属性
console.log(Object.keys(obj))
// 当使用 JSON.stringify() 时,以 symbol 值作为键的属性会被完全忽略:
console.log(JSON.stringify(obj))ES6的Object专门提供了一个方法用于获取对象的Symbol属性。
//其包含的是以 Symbol 为键的属性 console.log(Object.getOwnPropertySymbols(obj)); //[Symbol(obj的id), Symbol(obj的name), Symbol(obj的别名)]
1.2 内置的Symbol属性
除了自己创建的 symbol,JavaScript 还内建了一些在 ECMAScript 5 之前没有暴露给开发者的 symbol,它们代表了内部语言行为。它们可以使用以下属性访问:
1、迭代 symbols Symbol.iterator
一个返回一个对象默认迭代器的方法。被 for...of 使用。
object默认没有这个迭代属性,所以不能使用for...of,但是可以自己定义
//通过数组可以观察这个迭代器
const arr = [1, 2, 3];
console.log(arr[Symbol.iterator]); // f()
const it = arr[Symbol.iterator](); //{next: f(){}}
console.log(it.next()); //{value: 1, done: false}
console.log(it.next())
console.log(it.next())
console.log(it.next()); //{value: undefined, done: true}const myObj = {
name: '诸葛',
age: 18,
address: '卧龙岗',
[Symbol.iterator]: function () {
const _this = this;
let num = 0;
const keys = Object.keys(_this);
return {
next: function () {
return {
value: _this[keys[num++]],
done: num > keys.length
}
}
}
}
}
//现在可以使用for...of迭代obj了
for(const value of myObj){
console.log(value);
}2、Symbol.replace
一个替换匹配字符串的子串的方法。被 String.prototype.replace() 使用。
3、Symbol.split
一个在匹配正则表达式的索引处拆分一个字符串的方法.。被 String.prototype.split() 使用。
更多了解:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol
2、Set数据结构
Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。
Set对象是值的集合,Set 中的元素只会出现一次,即 Set 中的元素是唯一的。
NaN 和 undefined 都可以被存储在 Set 中,NaN 被视为相同的值(NaN 被认为是相同的,尽管 NaN !== NaN)。
2.1 创建一个set数据结构的实例
//Set 构造函数能创建 Set 对象实例
const mySet = new Set(); // Set(0) {size: 0}参数iterable 可选,如果传递一个可迭代对象,它的所有元素将不重复地被添加到新的 Set 中。如果不指定此参数或其值为 null,则新的 Set 为空,返回一个新的 Set 对象。
const mySet1 = new Set([1,2,2,3,3,4,5]);
console.log(mySet1);//Set(5) {1, 2, 3, 4, 5}2.2 实例的方法
2.2.1 添加add(value)
如果 Set 对象中没有具有相同值的元素,则 add() 方法将插入一个具有指定值的新元素到 Set 对象中。 并返回Set 对象本身,因此可以链式调用。
const mySet = new Set();
mySet.add(1).add(true).add('Tom').add(18)
console.log(mySet); //Set(4) {1, true, 'Tom', 18}2.2.2 移除某个值delete(value)
delete() 方法从 Set 对象中删除指定的值(如果该值在 Set 中)。
成功删除返回 true,否则返回 false。
console.log(mySet.delete('Tom')); // true
console.log(mySet.delete('daisy'));//false从Set中删除对象。
因为对象是通过引用比较的,所以如果没有对原始对象的引用,就必须通过检查单个属性来删除它们。
const setObj = new Set();
setObj.add({ x: 5, y: 20 }).add({ x: 20, y: 30 });
// 删除任何x > 10 的对象
setObj.forEach(point => {
if (point.x > 10) {
setObj.delete(point);
}
})
console.log(setObj)2.2.3 移除所有元素 clear()
clear() 方法移除 Set 对象中所有元素。 返回值为undefined.
mySet.clear()
2.2.4 has(value)
has() 方法返回一个布尔值来指示对应的值是否存在于 Set 对象中。
const setObj = new Set();
setObj.add({ x: 5, y: 20 }).add({ x: 20, y: 30 }).add('circle');
console.log(setObj.has({ x: 20, y: 30})); // false,不是一个对象的引用
console.log(setObj.has('circle')); //truehttps://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Set
2.3 实例的属性
size 属性将会返回 Set 对象中(唯一的)元素的个数。
size 的值是一个整数,表示 Set 对象有多少条目。size 的 set 访问函数是 undefined;你不能改变这个属性。 也就是只能读不能写。
2.4 遍历
2.4.1 forEach()
forEach() 方法对 Set 对象中的每个值按插入顺序执行一次提供的函数。
setObj.forEach(item => console.log(item))
2.4.2 for ... of
for(const item of setObj){
console.log(item);
}2.5 应用场景
2.5.1 和数组的转换
// 数组去重 const arr = [1,1,2,2,3,3,4,5]; //const newArr = Array.from(new Set(arr)); const newArr = [...new Set(arr)]; console.log(newArr); // [1, 2, 3, 4, 5]
2.5.2 和字符串相关
let str1 = 'javascript';
let str2 = 'JAvaScript';
let s1 = new Set(str1);
let s2 = new Set(str2);
//大小写敏感
console.log(s1); //Set(9) {'j', 'a', 'v', 's', 'c', …}
console.log(s2); //Set(10) {'J', 'A', 'v', 'a', 'S', …}3、Map数据结构
Map 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者基本类型)都可以作为一个键或一个值。
3.1 对键值的操作
// 创建一个map实例
const myMap = new Map();
// 这种方式赋值不能改变map的数据结构,所以不推荐
// myMap.name = 'mrszhao';
// 正确的方式,使用set方法
myMap.set('name', 'mrszhao');
myMap.set('age', 18);
myMap.set('city', '成都');
myMap.set('city', '重庆');
// 使用get方法获取键的值
console.log(myMap.get('city'));
// size属性获取键值对的数量
console.log(myMap.size);
// 删除键值对,返回布尔值
myMap.delete('age');
// 判断知否存在某个键,返回布尔值
console.log(myMap.has('name'))
console.log(myMap);注意:键名具有唯一性,重复设置,后面的值会覆盖前面的值,而且,键值对是根据设置的顺序保存的。
3.2 键名可以是任意的数据类型
object对象的键名只能是string和symbol,而map数据的键名可以是任意数据类型,包括复杂数据类型。
//对象的键名会被转成字符串,对象转成字符串是[object Object]
const obj = {
[Symbol()]: 'mrszhaoObj',
name: '诸葛',
1: 1,
true: true,
[{a: 1}]: 'a'
}
console.log(obj);
obj[{b: 1}] = 'b';
console.log(Object.keys(obj))
console.log(obj['[object Object]'])map的优势在于可以使用对象作为键名。
const o = {b: 2};
myMap.set({a: 1}, 'a');
myMap.set(o, 'b');
myMap.set(function a(){} , 1);
myMap.set(true, 1);
//对象的key只能出现一次 要用对象来设键名的时候,一定要在外面设变量
// 因为{} === {} false
console.log(map.get({a: 1})); //undefined
console.log(map.get(o)); // 'b'3.3 map的遍历
// 第一个参数是value,第二个参数是key myMap.forEach((value, key) => console.log(key, value));
因为拥有迭代器属性,可以使用for...of
// 遍历出来的item是一个包含key和value的数组
// 使用数组的解构方式
for(const [key, value] of myMap){
console.log(key, value)
}//只遍历键名。myMap.keys()返回一个有迭代器的对象,所以可以用for...of
for(const key of myMap.keys()){
console.log(key)
}
// 只遍历值
for(const value of myMap.values()){
console.log(value)
}3.4 map和数组的转换
const arrMap = [['key1', 'value1'], ['key2', 'value2']]; //使用常规的 Map 构造函数可以将一个二维键值对数组转换成一个 Map 对象 const myMap1 = new Map(arrMap);
// const newArr = Array.from(myMap1); //最简单的是使用...扩展运算符展开对象,再放入数组。 const newArr = [...myMap];
更多Map和Object对象的差别,可以查看文档:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Map
4、Object相关
4.1 Object.assign()
Object.assign(target, ...sources)
Object.assign() 方法将所有可枚举(Object.propertyIsEnumerable() 返回 true)的自有(Object.hasOwnProperty() 返回 true)属性从一个或多个源对象复制到目标对象,返回修改后的对象。
如果目标对象与源对象具有相同的 key,则目标对象中的属性将被源对象中的属性覆盖,后面的源对象的属性将类似地覆盖前面的源对象的属性。
const target = {
a: 1,
b: 2
}
const source1 = {
a: 2,
c: 3
}
const source2 = {
a: 3,
d: 4
}
const result = Object.assign(target, source1, source2);
console.log(result, result === target);点击空白页面产生小球,点击小球删除自己。试试。看到空白页面使劲点。
function Circle(option) {
option = option || {};
this.shape = {
r: 30,
bgColor: '#f60',
x: 0,
y: 0
}
Object.assign(this.shape, option);
}
Circle.prototype.removeCircle = function() {
console.log('我被干掉了');
}
const oHTML = document.documentElement;
oHTML.addEventListener('click', function (e) {
var circle = new Circle({
r: getRandom(20, 50),
bgColor: `rgba(${getRandom(0, 255)},${getRandom(0, 255)},${getRandom(0, 255)}, ${Math.random()})`,
x: e.x,
y: e.y,
})
console.log(circle);
const oDiv = document.createElement('div');
oDiv.style.cssText = `width:${circle.shape.r * 2}px;height:${circle.shape.r * 2}px;background-color:${circle.shape.bgColor};border-radius:50%;position:absolute;left:${circle.shape.x - circle.shape.r}px;top:${circle.shape.y - circle.shape.r}px;transition:0.2s`;
document.body.appendChild(oDiv);
oDiv.addEventListener('click', function(e) {
this.remove();
circle.removeCircle();
e.stopPropagation();
})
})
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}4.2 Object.is()
Object.is() 方法判断两个值是否为同一个值。
Object.is(value1, value2);
Object.is的判断规则和==和===都不同。
console.log(Object.is(+0, -0), +0 === -0); // false true console.log(Object.is(NaN, NaN), NaN === NaN); //true false console.log(Object.is(undefined, null), undefined == null); //false true
4.3 Object.create()
Object.create() 方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型(prototype)。
比如下面这个案例,点击页面产生一个圆,点击圆本身,会弹出它的面积。试试。
核心代码:
// 创建一个大类 圆,有半径,x坐标, y坐标三个属性
function Circle(r = 20, x = 0, y = 0) {
this.r = r;
this.x = x;
this.y = y;
}
// 有一个原型上的获取面积的方法
Circle.prototype.getArea = function () {
return Math.floor(Math.PI * (this.r ** 2));
}
// 创建一个子类,产生有背景颜色的圆
function BgcolorCircle(bgcolor = '#000') {
this.bgcolor = bgcolor;
// 执行大类的构造函数,让子类也有这三个属性,并且有默认值
Circle.call(this);
}
// 把大类的原型对象当作小类的原型对象的原型,小类的实例也可以访问大类原型上的方法
BgcolorCircle.prototype = Object.create(Circle.prototype);
// 如果不指定构造函数,会默认指向Circle上级对象
BgcolorCircle.prototype.constructor = BgcolorCircle;
const oHTML = document.documentElement;
oHTML.addEventListener('click', function (e) {
const cc = new BgcolorCircle();
cc.r = getRandom(10, 100);
cc.x = e.x;
cc.y = e.y;
cc.bgcolor = `rgba(${getRandom(70, 255)},${getRandom(70, 255)},${getRandom(70, 255)})`;
const oDiv = document.createElement('div');
oDiv.style.cssText = `width: ${cc.r * 2}px;height:${cc.r * 2}px;position:absolute;left:${cc.x - cc.r}px;top:${cc.y - cc.r}px;background-color: ${cc.bgcolor};border-radius:50%`;
document.body.appendChild(oDiv);
oDiv.addEventListener('click', function (e) {
alert(`我的面积是:${cc.getArea()}`);
e.stopPropagation();
})
})
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}5、class类
看看Class类这个语法糖有多甜。
5.1 声明类
// 类声明,没有提升,先声明再实例化
class Circle {
//用于创建和初始化一个由class创建的对象
constructor(r = 20, x = 0, y = 0) {
this.r = r;
this.x = x;
this.y = y;
}
// getter 只读属性
get area() {
return this.getArea();
}
// 原型方法
getArea() {
return (Math.PI * (this.r ** 2)).toFixed(2);
}
}
const c = new Circle(50);
console.log(c);
console.log(c.area);
console.log(c.getArea());5.2 静态属性和方法
不能在类的实例上调用静态方法,而应该通过类本身调用。
// 类声明,没有提升,先声明再实例化
class Circle {
//用于创建和初始化一个由class创建的对象
constructor(r = 20, x = 0, y = 0) {
this.r = r;
this.x = x;
this.y = y;
}
// getter 只读属性
get area() {
return this.getArea();
}
// 静态属性,只有类能访问,实例不能访问
static proname = 'Circle';
// 原型方法
getArea() {
return (Math.PI * (this.r ** 2)).toFixed(2);
}
// 静态方法
static createDefaultCircle() {
return new Circle();
}
}
// 只能类访问静态属性和静态方法
console.log(Circle.proname);
console.log(Circle.createDefaultCircle());5.3 extends扩展子类
extends 关键字用于类声明中,以创建一个类,该类是另一个类的子类。
class ChildClass extends ParentClass { ... }// 使用extends扩展内置对象,产生一个内置对象的子类
class myDate extends Date {
constructor(date = new Date()) {
// super 关键字用于调用对象的父对象上的函数。
// 调用超类构造函数并执行。相当于Circle.call(this);
// 并把子类的参数传递给父类。
super(date);
}
getFormattedDate() {
const week = ['星期天', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
let date = this.getDate();
let month = this.getMonth() + 1;
let year = this.getFullYear();
date = date < 10 ? '0' + date : date;
month = month < 10 ? '0' + month : month;
return `${year}.${month}.${date} ${week[this.getDay()]}`;
}
}
const mydate = new myDate();
console.log(mydate.getFormattedDate());
const mydate1 = new myDate('2023-10-1')
console.log(mydate1.getFormattedDate());扩展Circle的子类对象
class ColorCircle extends Circle {
constructor(bgcolor = '#000') {
// 如果子类中定义了构造函数,那么它必须先调用 super() 才能使用 this 。
super();
// console.log(super());//返回了子对象,后面才能使用this来指向子对象。
this.bgcolor = bgcolor;
}
}
const bgcircle = new ColorCircle('#f30');
console.log(bgcircle);
bgcircle.r = 30;
console.log(bgcircle.area);
class BorderCircle extends Circle {
constructor(border = '1px solid #000') {
super();
this.border =border;
}
}
const bordercircle = new BorderCircle();
console.log(bordercircle);
bordercircle.border = `5px solid ${bgcircle.bgcolor}`
bordercircle.r = 40;
console.log(bordercircle.area);现在Class类的写法比起以前原型的复杂写法看起来要清爽很多。
还有一些高级的内容,比如Promise异步、代理等等,后面有时间再写了。
发表评论:
◎请发表你卖萌撒娇或一针见血的评论,严禁小广告。