es6的一些骚操作

  • 简洁高效的解构语法
  • 遍历写法(数组与对象)
  • 定义与获取对象属性描述
  • 总结

前言

写博客,在犹豫一会,价值度不高,非知识开拓性,无非几十分钟,几个小时写完的作文一样,点击发布交卷,之后期待别人的评分点赞?技术重复分享,搞得像是无耻的事一样,不,我也这么这么做,反正也闲的蛋疼,嘻嘻嘻。其实想多了,纯粹的分享,才不会想或者在乎呢,分享个人经验与看法,或是分享已有的知识见解,都是个体情感产生的,万一有一个小弟看着,就与你产生共契合,帮助到别人,当然是很好的。写博客,其实想表达的,自己对知识点的见解而已,虽然粗陋,也是给自己看的,谁会嫌弃自己的佳作的呢。

简洁高效的语法

es6新增了很多简洁风格,使其代码清爽便捷了许多。

模版字符串

模版字符串很常用,分两类应用场景。
一,书写整齐的HTML片段,避免了‘一行到底’与字符串拼接;
二,应用于字符串与变量随性组合拼接。

比较常用,附上小例子

const name = '章';
$('.container').prepend(
    '<div>' + 
        '<span> 额hi额黑' + name + '</span>' +
    '</div>'
)
// 模版字符串
$('.container').prepend(`
    <div>
        <span>额hi额黑 ${name}</span>
    </div>
`)
函数传参默认值

定义函数的时候,有时或者需求上需将形参设置为指定的备选值或叫默认值。

小例子,结合具名函数与匿名函数,以及用上模版字符串

function person (name,species) {
    species = species || 'person';
    console.log(species + ',It name is:' + name);
}
const person = function (name,species = 'person') {
    console.log(`${species},It name is:${name}`);
}

注意:函数形参不能有同名参数,会报错。
也是,当然直接这么用,也体现不了多少简洁或是方便。函数默认赋值,一般与解构赋值结合

// 函数参数为对象
function foo({x,y=5}) {
    console.log(x,y);
}
foo(); // 报错,实参不为对象肯定报错呗
foo({}); // undefind 5; 函数默认值为5

// 简单网络请求的封装函数,函数格式(字符串,对象)
function fetch(url,{ body = '', method = 'get', headers = {} }) {
    console.log(method);
}
fetch('https://www.hp.com',{});  // 'get',函数默认值为get
fetch('https://www.hp.com');     // 传格式不对,报错

// 上述例子,格式定要传对象,否则缺少参数就会报错,可以改良,给对象也一个默认值
function fetch(url,{ body = '', method = 'get', headers = {} } = {}) {
    console.log(method);
}
fetch('https://www.hp.com');  // ‘get’

// 若指定某个参数不能省略,又想自定义抛出错误(默认值应用)
function customError () {
    throw new Error('必须传一个对象,否则我就报错这条错误信息。');
}
 function foo({x,y=5} = customError()) {
    console.log(x,y);
}
foo(); // 报错,必须传一个对象,否则我就报错这条错误信息  
扩展运算符

扩展运算符,一般与es6新增的rest结合使用。rest参数就类似于arguments对象,收集实参信息,(形式...变量名),就想当于将实参里面每项罗列出来。

来个小例子,运行下

(function (){
    console.log(arguments); //{ '0': 1, '1': 2, '2': 3, '3': 4, '4': 'a', '5': 2, '6': 3 }
    Object.values(arguments).forEach(v => {
        console.log(v);
    });
})(1,2,3,4,'a',2,3);

(function (...values){
    console.log(values); // [ 1, 2, 3, 4, 'a', 2, 3 ]
    values.forEach(v => {
        console.log(v);
    })
})(1,2,3,4,'a',2,3);

(function (...values){
    console.log(values); // [ { a: 1, b: true, c: 'hello' }, { b: 1 } ]
    values.forEach(v => {
        console.log(v);
    })
})({a:1,b: true, c: 'hello'},{b: 1})

上述代码,写了个匿名函数传参自调用,可以看出arguments参数是一个实参对象。键值对形式,键为位置下标。而使用rest参数,可以获得实参选项对应数组,便于遍历。

遍历写法(数组与对象)

遍历,在平平常常的开发中,使用频率蛮高的。我们总会喜欢一种或几种写法,这无可厚非。但是常用不一定合适,或是不一定一次性代码解决需求问题。所以下面总结,遍历所有场景与方法,便于冲锋般的急促中,想起身上还有一个不常用但迅速杀一窝敌的手雷。崩————GG,阵亡

1. for循环

for 循环,想必大学听着基本语法结构。但是好像在实际开发中,没怎么用过。反正我是没用过,但是很有用滴~

写个例子

const arr = [1,2,1,'c',true];
for(let i =0; i < arr.length;i++) {
    console.log(arr[i]); // 循环获取数组的值与下标
}
2. forEach循环

forEach循环,数组遍历常用的一种。注意,返回值为undefined;唯一能够跳出循环的,通过try{}catch(err) { throw ' Error Info'}类似抛出异常结构,终止循环(注意的是,将遍历循环代码放在try...catch结构内)。不会响应break、continue、return语句。其中break,结束循环的进程;continue,结束本轮循环;return,跳出函数并返回值。

写个例子

const traverseArr = ['1','2', '3', 'asdf',true, 2];
let resultStr = traverseArr.forEach((item,index,array) => {
    console.log(item); // 数组当前选中值
    console.log(index); // 数组当前选中的下标
    if(item === 'asdf') {
        console.log('数组里面的true,2不会打印!');   // 没有效果,不响应break语句
        break;
    }
    console.log(array); // 数组本身
})
console.log(resultStr); // undefined
-----------
try {
    let resultStr = traverseArr.forEach((item,index,array) => {
        console.log(item);
        if(item === 'asdf') {
            throw 'asdf值以后的值,不会打印了。结束循环了'
        }
    })
} catch (err) {
    console.log(err);
}
3.for... of遍历

for...of遍历数组 , 可以正确响应break、continue和return语句

写个例子

const traverseArr = ['1','2', '3', 'asdf',true, 2];
for(item of traverseArr) {
    console.log(item);
    if(item === 'asdf') {
        console.log('确实不会打印asdf里面的值!');
        break;
    }
}
4.map遍历

map遍历是很常用的一种,支持返回值为处理后的数组,数组中的元素为原始数组元素调用函数处理后的值。 可以发现,返回值为深拷贝

写个例子

const traverseArr = ['1','2', '3', 'asdf',true, 2];
let resultArr = traverseArr.map((item,index,arr) => {
    if(item === '3') {
        return '2';
    }
    return item;
});
console.log(traverseArr);  // [ '1', '2', '3', 'asdf', true, 2 ]
console.log(resultArr);    // [ '1', '2', '2', 'asdf', true, 2 ]
5.filter遍历

filter遍历, 不会对空数组进行检测,不会改变原始数组,返回新数组。一般用于过滤数组

写个例子

const traverseArr = [
    {name: 'yy',age: 24, denger: 'man',isFirst: true},
    {name: 'lz', age: 23,denger: 'man',isFirst: false},
    {name: 'laz', age: 23,denger: 'woman',isFirst: true},
]
let resultArr = traverseArr.filter((v,i,arr) => v.denger === 'woman');
console.log(traverseArr);   // [ { name: 'yy', age: 24, denger: 'man', isFirst: true },{ name: 'lz', age: 23, denger: 'man', isFirst: false },
{ name: 'laz', age: 23, denger: 'woman', isFirst: true } ]
console.log(resultArr);  // [ { name: 'laz', age: 23, denger: 'woman', isFirst: true } ]
6.every遍历

every遍历,用于检测数组所有元素是否都符合指定条件,如果所有元素都满足条件,则返回 true。其实蛮有作用的,一开始我们可能设置标志位,然后遍历每一项,来更改标志位状态。与some遍历相比,相当于逻辑与的操作

写个例子

const traverseArr = [
    {name: 'yy',age: 24, denger: 'woman',isFirst: true},
    {name: 'lz', age: 23,denger: 'woman',isFirst: false},
    {name: 'laz', age: 23,denger: 'woman',isFirst: true},
];
let isAllWomen = traverseArr.every((v,i,arr) => {
    if(v.denger === 'woman') {
        return true;
    }
    return false;
})
console.log(isAllWomen); // true
7.some遍历

some遍历, 用于检测数组中的元素是否用满足指定条件的,如果有一个元素满足条件,剩余的元素不会再执行检测。返回值,若如果数组中有元素满足条件返回true,且终止循环

写个例子

const traverseArr = [
    {name: 'yy',age: 24, denger: 'woman',isFirst: true},
    {name: 'lz', age: 23,denger: 'man',isFirst: false},
    {name: 'laz', age: 23,denger: 'man',isFirst: true},
];
let isHasWomen = traverseArr.some((v,i,arr) => {
    console.log(v);
    if(v.denger === 'woman') {
        return true;
    }
    return false;
})
console.log(isHasWomen);  
8.reduce遍历

reduce遍历,接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。返回值,为返回计算结果

写个例子

// 获取数组值得总和
const traverseArr = [
    {name: 'yy',age: 24, denger: 'woman',isFirst: true},
    {name: 'lz', age: 23,denger: 'man',isFirst: false},
    {name: 'laz', age: 21,denger: 'man',isFirst: true},
];
let totalAge = traverseArr.reduce((total,v,i,arr) => {
    return total + v.age;
},0);
console.log(totalAge); // 68
--------
// 获取数组最大值
const traverseArr = [
    {name: 'yy',age: 24, denger: 'woman',isFirst: true},
    {name: 'lz', age: 23,denger: 'man',isFirst: false},
    {name: 'laz', age: 21,denger: 'man',isFirst: true},
];
let maxVal = traverseArr.reduce((prev,cur) => {
    return Math.max(prev,cur.age)
},0);
console.log(maxVal); // 24
9.reduceRight遍历

reduceRight遍历,方法的功能和reduce()功能是一样的,不同的是reduceRight()从数组的末尾向前将数组中的数组项做累加。

10.for...in遍历

in 关键字遍历对象属性和原型上的属性(可遍历属性)。for...in遍历,一般用于遍历对象属性,但也可以遍历数组

写个例子

const traverseArr = [
    {name: 'yy',age: 24, denger: 'woman',isFirst: true},
    {name: 'lz', age: 23,denger: 'man',isFirst: false},
    {name: 'laz', age: 21,denger: 'man',isFirst: true},
];
const objH = {
    name: 'name',
    age: 23,
    gender: 'man'
}
for(is1 in traverseArr) {
    console.log(traverseArr[is1]);
}
for(i in objH) {
    console.log(traverseArr[is1]);
}
11.es6新增 keys,values,entries

ES6 提供三个新的方法 —— entries(),keys()和values() —— 用于遍历数组。它们都返回一个遍历器对象

写个例子

const arr = [2,true,'hello'];
console.log(arr.values());  // Object [Array Iterator] {}
console.log(arr.keys());  // Object [Array Iterator] {}
console.log(arr.entries());  // Object [Array Iterator] {}

for(let [v,i] of arr.entries()) {
    console.log(v);  // 下标
    console.log(i);  // 值
}
for(let v of arr.keys()) {
    console.log(v); // 值
}
for(let v of arr.values()) {
    console.log(v); // 值
}
12.find遍历

find遍历,返回通过测试(函数内判断)的数组的第一个元素的值,注意,当数组中的元素在测试条件时返回 true 时, find() 返回符合条件的元素。跟some遍历类似,符合条件停止遍历,但some遍历返回的状态

写个例子

const arr = [2,3,true,'hello','xixi',2];

let firstVal = arr.find((val,index,arr) => {
    console.log(val);
    if(val === 'hello') {
        return true;
    }
}); // 遍历打印: 2 3 true hello

console.log(firstVal); // hello
13.findIndex 遍历

findIndex遍历, 返回传入一个测试条件(函数)符合条件的数组第一个元素位置(下标)。

14.对象遍历 Object.keys()、Object.values()、Object.entries()

我们知道<code>in</code>关键字,遍历对象属性但也遍历该对象原型链上的可遍历属性。而现在介绍的属性,遍历的是本身对象可遍历的属性。

写个例子

const obj = {
name: 'yangyi',
age: 23,
gender: 'woman',
say() {
    console.log('hello beatuify world!');
}
}; 
const parent = {
    sex: true,
    sayHei: function (name) {
        console.log(`hello ${name}!`);
    }
};

Object.setPrototypeOf(obj,parent); // 建立原型链

// Object.keys()
console.log(Object.keys(obj));  // [ 'name', 'age', 'gender', 'say' ]
Object.keys(obj).forEach(v => {
    console.log(v); // 打印: name,age,gender,say
});
// for ... in 可遍历原型上的属性
for(key in obj) {
    console.log(key);  // 打印对象本身及原型上的key值
}  
----------
// Object.values()
console.log(Object.values(obj));
Object.values(obj).forEach(v => {
    console.log(v)
})
----------
// Object.entries()
console.log(Object.entries(obj)); // [ [ 'name', 'yangyi' ],[ 'age', 23 ],[ 'gender', 'woman' ],[ 'say', [Function: say] ] ]
for([k,v] of Object.entries(obj)) {  
    console.log(k);
    console.log(v);
}

注意的是,使用Object.entries()返回二维数组格式。我们也可以通过Object.fromEntries()将一个键值对数组转为对象。

定义与获取对象属性描述

本小节,主要了解的是对象本身属性描述,定义描述与获取描述。其中,介绍一下,属性描述符主要分为两类,数据描述符和存取描述符。数据描述符是一个具有值的属性,而存取描述符是由getter-setter函数对描述的属性。

了解一下具体属性描述符:

  1. configurable,该值为 true 时,该属性描述符才能够被配置改变,否则无法重新设置。
  2. enumerable, 该值为true时,该属性才能够出现在对象的枚举属性中
  3. value, 该属性对应的值,默认为undefined。
  4. writable,该属性的writable值为true,value才能被修改。
  5. get, 属性提供 getter 的方法,如果没有 getter 则为 undefined。
  6. set, 属性提供 setter 的方法,如果没有 setter 则为 undefined.

注意,描述符(value或writable)和(get或set)关键字不能同时存在,否则会抛出一个错误。如果一个描述符不具有value,writable,get 和 set 任意一个关键字,那么它将被认为是一个数据描述符

大致了解了属性描述符,现在我们看看与它有关的方法:

Object.defineProperty()和Object.defineProperties(),定义对象的属性描述符

写个小例子,定义一个对象的属性的值不可修改,不可遍历,不可重新配置

let descriptorObj = {};
let descriptor = Object.create(null);
// 默认没有 enumerable,没有 configurable,没有 writable
descriptor.value = 'static';
Object.defineProperty(descriptorObj,'key',descriptor);
console.log(descriptorObj.key);
descriptorObj.key = 'write';  
console.log(descriptorObj.key); // 打印static,不可修改

for(key in descriptorObj) {
    console.log(key,'打印'); // 不可遍历
};

Object.defineProperty(descriptorObj,'key',{  // 报错
    value: '重新修改这个值',
    enumerable: true,
    writable: true,
    configurable: true
})

注意上述,通过 Object.create(null)将proto属性指向null,然后指定对象value描述符,其它描述符不指定,则默认false。如果想同时配置对象的多个属性的属性描述符,可以使用Object.defineProperties()方法

let descriptorObj = {
name: 'yangyi',
age: 24,
denger: 'woman'
};

Object.defineProperties(descriptorObj, {
    name: {
        value: 'yangyi1',
        writable: true,
        enumerable: false,
        configurable: true
    },
    age: {
        value: 25,
        enumerable: true,
        writable: false,
        configurable: true
    },
    denger: {
        value: 'man',
        writable: false,
        enumerable: true,
        configurable: false
    }
});
console.log(Object.getOwnPropertyDescriptors(descriptorObj));

Object.keys(descriptorObj).forEach(v => {
    console.log(v); // age denger
});

console.log(descriptorObj.age); // 25
descriptorObj.age = 26; // age属性描述符不可重写
console.log(descriptorObj.age); // 25

Object.defineProperty(descriptorObj,'denger',{  // 报错
    writable: true,
    configurable: true
})

上述定义对象多个属性的描述符代码中,使用getOwnPropertyDescriptors获取对象属性的描述符。下面就介绍获取对象描述符的方法

Object.getOwnPropertyDescriptors() 和Object.getOwnPropertyDescriptor(),获取对象的属性描述符
let descriptorObj = {
name: 'yangyi',
age: 24,
denger: 'woman'
};

Object.defineProperties(descriptorObj, {
    name: {
        value: 'yangyi1',
        writable: true,
        enumerable: false,
        configurable: true
    },
    age: {
        value: 25,
        enumerable: true,
        writable: false,
        configurable: true
    },
    denger: {
        value: 'man',
        writable: false,
        enumerable: true,
        configurable: false
    }
});
console.log(Object.getOwnPropertyDescriptors(descriptorObj));
---打印值--
{ name: 
    { 
        value: 'yangyi1',
        writable: true,
        enumerable: false,
        configurable: true 
    },
age: 
    {
        value: 25,
        writable: false,
        enumerable: true,
        configurable: true 
    },
denger: 
    {
        value: 'man',
        writable: false,
        enumerable: true,
        configurable: false 
    } 
}

console.log(Object.getOwnPropertyDescriptor(descriptorObj,'denger'));
---打印值----
{ 
    value: 'man',
    writable: false,
    enumerable: true,
    configurable: false 
}

总结

怎么说呢,分享的其实是技术面的扩展,便于记忆,以及工作开发效率。其实真正的技术,还是不断深究其里,每个技术的细枝末节。不过始终相信,保持热情、耐心,一点点的去进步,相信会获得更多。

爱代码,爱生活


参考
es6 阮一峰

laozhang.jpg

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容