第1章 声明方式
一.声明变量let
- 没有预解析,不存在变量提升
- 一定要先定义再使用。(在代码块内,只要用let定义变量,在let定义之前使用都会报错)
- 同一个作用域内不能重复定义变量
- for循环里面是父级作用域,里面又是一个
二.块级作用域
- { //一个大括号包裹就是一个块级作用域 }
- {{{let a = 12; }}}
- if(){xxx}
- for(){}
- while(){}
三.声明常量const
- 定义好了不能改变
- const定义完变量必须有值,不能定义后再赋值,不能修改
- Object.freeze(对象);可以冻结对象,防止对象被修改
- 下面这是一个对象,对象是引用的关系可以被修改
const config = {
host:
username:
password:
version:
}
- eg.引用不可改的模块
const http = require('http');
第2章 解构赋值
es6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。
做数据交互是非常有用
一.数组解构
- 左右两边结构格式要保持一致
let [a,b,c] = [12,5,6];
二.对象解构
let {name,age,job} = {
name:'Strive',
age:18,
job:'teacher'
}
- 解构可以设置别名
let {name:n,age:g,job:a} = jsonData; - 解构时可以设置默认值
let [a,b,c="暂无数据"] = [1,2];
- eg.获取模块的指定方法?
- eg.交互变量的值
let x = 1,y = 2;
[x,y]=[y,x];
- eg.从函数中返回多个值?
function fn(){ return [1,2,3] }
let [a,b,c] = fn();
- eg.undefined和null的区别?
- undefined相当于什么都没有,null相当于有值,但值为null。
let [a,b="JSPang"]=['技术胖',undefined];
let [c,d="JSPang"]=['技术胖',null];
console.log(a,b,c,d);//技术胖 JSPang 技术胖 null
三.字符串解构
字符串也可以解构,因为此时字符串被转换成了一个类似数组的对象。
const [a,b,c,d,e,f]="JSPang";
console.log(a)//J
四.函数参数解构
[[1, 2], [3, 4]].map(([a, b]) => a + b);// [ 3, 7 ]
五.圆括号的使用
如果解构之前就定义了变量,这时再解构就会报错。
解决方法:在解构的{}外边加一个圆括号
let foo;
({foo} ={foo:'JSPang'});
console.log(foo); //JSPang
第3章 字符串的扩展
一.字符串模板
优点是可以随意换行,添加变量${变量名字}
字符串模板可以嵌套
二.标签模板
- 模板字符串可以紧跟在一个函数名后面,该函数将被用来处理这个模板字符串。
alert `hello`==>等同于alert(["hello"])
- eg.多语言转换(国际化处理)
i18n`Welcome to ${siteName}, you are visitor number ${visitorNumber}!`
// "欢迎访问xxx,您是第xxxx位访问者!"
- eg.过滤 HTML 字符串,防止用户输入恶意内容
let sender = '<script>alert("abc")</script>'; // 恶意代码
let message = SaferHTML`<p>${sender} has sent you a message.</p>`;
function SaferHTML(templateData) {
let s = templateData[0];
for (let i = 1; i < arguments.length; i++) {
let arg = String(arguments[i]);
//在替换中转义变量的特殊字符
s += arg.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
s += templateData[i];
}
return s;
}
console.log(message);
//'<p><script>alert("abc")</script> has sent you a message.</p>'
三.字符串方法
- 字符串查找
- str.indexOf(要找的字符) 返回索引位置,没找到返回-1
- str.includes(要找的字符) 返回true/farse
- eg.判断浏览器
if(navigator.userAgent.includes('Chrome')){}
- 字符串是否以谁开头
- str.startWith(检测字符)
- 字符串是否以谁结尾
- str.endsWith(检测字符)
- 重复字符串
- str.repeat(重复次数)
- 填充字符串
- str.padStart(整个字符串长度,填充字符) 往前填充字符串
- str.padEnd(整个字符串长度,填充字符) 往后填充字符串
- eg.
"x".padStart(5,"a") //"aaaax" - eg.
str01.padStart(str01.length+str02.length,str02)
- 消除空格
- str.trim() 消除左右空格
- str.trimStart() 消除字符串头部空格
- str.trimEnd() 消除字符串尾部的空格
第4章 函数的扩展
一.函数参数默认值
function fn({x=0,y=0}={}){
alert(x,y);
}
fn();
- 作用域
- 一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域。
- 函数参数设置默认值就是已经定义了,不能再使用let,const声明
function fn(a=10){
let a = 101; //这是错误的
}
- 与解构赋值默认值结合使用
function fn({x=0,y=0}){
alert(x,y);
}
fn({x:1,y:2});
- 参数默认值的位置
- 定义了默认值的参数应该是函数的尾参数。否则报错。
- length属性
- 含义是该函数预期传入的参数个数。
- 指定默认值的参数和rest参数不计入length中
(function (a, b, c = 5) {}).length // 2(function(...args) {}).length // 0- 如果设置了默认值的参数不是尾参数,那length属性也不再计入后面的参数
(function (a, b = 1, c,d) {}).length // 1
二.rest参数
rest参数(形式为...变量名)用于获取函数的多余参数,这样就不需要使用arguments对象了。
- 用来展开数组
[1,2,3] ——> ...[1,2,3] ——> 1,2,31,2,3 ——> ...1,2,3 ——> [1,2,3]
- 用来获取剩余参数:必须放到最后
[a,b,...c] = [1,2,3,4,5] //==>c的值为3,4,5
- eg.复制数组
let arr2 = [...arr1];或者let [...arr2] = arr1;let arr2 = Array.from(arr1);
- eg.合并数组(都是浅拷贝)
let arr1=[1],arr2[2],arr3=[3,4,5];let merge = [...arr1,...arr2,...arr3] //==>[1,2,3,4,5]ES5:let merge = arr1.concat(arr2,arr3);
- eg.用push()追加数组
let arr1=[1,2,3],arr2=[4,5];arr1.push(...arr2); //==>arr1的值 [1, 2, 3, 4, 5]ES5:Array.prototype.push.apply(arr1,arr2); //==>arr1的值 [1, 2, 3, 4, 5]
- eg.替代apply():求数组中最大值
Math.max(...[14,3,5]);或者Math.max(14,3,5);ES5:Math.max.apply(null,[14,3,5])
- eg.将字符串转换成字符串数组
alert( [...'hello']); //==>["h","e","l","l","o"]
- eg.只有函数调用时,...才可以放到圆括号中否则会报错
let a = (...[1,2]) //==>报错
三.name属性
name属性是返回该函数的函数名。
const bar = function baz() {}; bar.name //"baz"var f = function(){}; f.name //"f"
四.箭头函数=>
es6允许使用箭头(=>)来定义函数
- 如果箭头函数只有一个参数时
- 使用格式 一个参数 => 只有一条执行语句;
- eg.
var f = v => v; //等同于 var f = function(v){ return v };
- 如果箭头函数没有参数或者有多个参数时,就用一个圆括号来代表参数部分。
- 使用格式 (无参数或多个参数) => 只有一条执行语句;
var f = () => 5; //等同于 var f = function(){ return 5 };-
var sum = (n1,n2) => n1 + n2; //等同于 var sum = function(n1,n2){ return n1 + n2; };
- 如果箭头函数的代码块有多条语句时,就用大括号将代码块括起来,并使用return语句返回。
使用格式 (无参数或多个参数) =>{
执行语句
return返回值
}
- 由于大括号被解释为代码块,所以箭头函数直接返回一个对象时,外边必须使用圆括号否则报错。
- eg.let getItem = id => ({id:"9412",name:"lining"});
- eg.箭头函数与变量解构结合使用
let full = ({first,last}) => first+"是名字,姓氏为"+last;
//等同于
let full = function(person){
return person.first+"是名字,姓氏为"+person.last;
}
- eg.用箭头函数来简化回调函数
[1,2,3].map(x => x*x);
//等同于
[1,2,3].map(function(x){return x*x});
var result = values.sort((a,b) => a-b);
//等同于
var result = values.sort(function(a,b){
return a-b;
})
- 函数体内的this对象,定义函数所在对象,不再是运行时所在的对象。
- 不可以当作构造函数,也就是说,不可以使用new命令否则报错。
- 不可以使用arguments对象,该对象在箭头函数体内不存在。如果要用,可以用reset参数代替。
- 不可以使用yield命令,因此箭头函数不能当作Generator函数。
- 函数参数的尾逗号 ES6允许函数的最后一个参数有尾逗号。
function clownEverywhere(
param1,
param2,
){ /*....*/}
- Function.prototype.toString()
- 方法是返回函数本身
- catch命令的参数可以省略
try{
/*...*/
}catch(err){
//处理错误
}
// 现在允许写成
try {
// ...
} catch {
// ...
}
第5章 数组的扩展
一.数组各种循环
- arr.forEach() 代替普通for循环
- 在forEach中使用return无效它还是会循环全部数组
arr.forEach(function(val,index,arr){ //... })
- arr.map() 映射(更新)做数据交互非常有用
- 正常情况下,需要配合return返回一个新数组
- 只有用map循环一定要有return
- eg.拼接DOM
- eg.重新整理数据结构[{title:'andy'}] ——> [{t:'他叫andy'}]
let newArr = arr.map((item,index,arr)=>{
let json = {};
json.t = `他叫${item.title}`;
return json;
})
alert(newArr); //==>{t:'他叫andy'}
- arr.filter() 过滤
- eg.过滤掉不合格的元素,如果回调函数返回true,元素就留下来
let newArr = [2,3,6].filter((item)=>{
return item>2 && item<5;
})
alet(newArr); //==>3
- arr.some() 类似循环查找
- eg.查找数组里某个元素符合条件就返回true
let result = [1,5,6].some((item,index)=>{
return item.toString().indexOf(5) > -1
})
alert(result); //==>true
- arr.every() 类似循环查找
- eg.查找数组里所有元素符合条件就返回true
- arr.reduce() 归并方法 从左往右遍历
- arr.reduceRight() 从右往左遍历
二.数组方法
- Array.from() 把类数组对象转成数组
- 这个东西具备length,就可以使用from
let str = 'Strive';
let arr = str.split('');//==> ["S", "t", "r", "i", "v", "e"]
let arr = Array.from(str); //==> ["S", "t", "r", "i", "v", "e"]
- Array.of() 类似于...扩展运算符,把一组值转成数组
- eg.
let arr = Array.of('a','b','c'); - eg.
alert(arr); //==> ['a','b','c']
- eg.
- arr.find() 查找
- eg.找出第一个符合条件的元素,没找到返回undefined
let res = [1,5,6].find((val,index,arr)=>{
return val>5;
})
alert(res); //==>6
- arr.findIndex() 查找
- 找出第一个符合条件的元素位置,没找到返回-1
- arr.fill(填充内容,【开始位置,结束位置】) 填充
- arr.indexOf()
- arr.includes()
三.数组的扩展运算符
-
扩展运算符(spread)是三个点(...)。将一个数组转为用逗号分隔的参数序列。
主要用于函数调用。
function add(x,y,z){
return x + y + z;
}
const num = [1,2,3];
add(...num);
- 扩展运算符后面还可以放置表达式。
const arr = [
...(x > 0 ? ['a'] : []),
'b',
];
- 如果扩展运算符后面是一个空数组,则不产生任何效果。
- [...[], 1] // [1]
- 只有函数调用时,扩展运算符才可以放在圆括号中,否则会报错。
- 替代函数apply()方法
Math.max.apply(null, [14, 3, 77])// ES5 的写法Math.max(...[14, 3, 77])// ES6 的写法Math.max(14, 3, 77);// 等同于
- 通过push函数,将一个数组添加到另一个数组的尾部。
var arr1 = [0, 1, 2]; var arr2 = [3, 4, 5]; // ES5的 写法Array.prototype.push.apply(arr1, arr2); // ES6 的写法 arr1.push(...arr2);
- 复制数组
const a1 = [1, 2];const a2 = [...a1];// 写法一const [...a2] = a1;// 写法二
- 合并数组
const arr1 = ['a', 'b'];const arr2 = ['c'];const arr3 = ['d', 'e'];// ES5 的合并数组arr1.concat(arr2, arr3);// [ 'a', 'b', 'c', 'd', 'e' ]// ES6 的合并数组[...arr1, ...arr2, ...arr3]// [ 'a', 'b', 'c', 'd', 'e' ]
- 与解构赋值结合
- 扩展运算符可以与解构赋值结合起来,用于生成数组。
let list = [1,2,3,4,5]let [a, ...rest] = listconsole.log(rest);//[ 2, 3, 4, 5 ]- 如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。
- 字符串
- 扩展运算符还可以将字符串转为真正的数组。
[...'hello']// [ "h", "e", "l", "l", "o" ]
- 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"
四.Array.prototype.sort()
参数可选,是用来规定排序的顺序,但必须是函数。
- 默认按照字母顺序排序
var arr = ['tom','ani','love']; arr.sort()//["ani", "love", "tom"]
- 升序排列
function sortNum(a,b){
return a-b
}
var arr = [12,323,1000,50]
arr.sort(sortNum)//[12, 50, 323, 1000]
- 降序排列
function sortNum(a,b){
return b-a
}
var arr = [12,323,1000,50]
arr.sort(sortNum)//[1000, 323, 50, 12]
- 按照数组对象中某个属性值进行排序
var arr = [
{name:'aaa',age:34},
{name:'sss',age:30},
{name:'ddd',age:60}
]
function compare(prop){
return function(a,b){
var value1 = a[prop];
var value2 = b[prop];
return value1 - value2;
}
}
arr.sort(compare('age'))
- 根据参数来确定是升序还是降序
function sortBy(attr,rev){
if(rev == undefined){
rev = 1;
}else{
rev = (rev) ? 1 : -1;
}
return function(a,b){
a =a[attr],b = b[attr];
if(a<b){ return rev * -1; }
if(a>b){ return rev * 1; }
return 0;
}
}
arr.sort(sortBy('age'),true)//升序
第6章 对象的扩展
一.对象简洁写法
let name = 'andy';
let json ={
name:name,
showA:function(){
return this.name;
}
}
简写方法如下:
let newJson = {
name, //name:name属性名就是变量名,属性值就是变量值
showA(){ //一般不要用箭头函数
return this.name;
}
}
alert( newJson.showA() );
- 简写的对象方法不能用作构造函数(new),会报错.
二.属性名表达式
用来定义对象的属性
- 直接用标识符作为属性名
obj.tip = true; - 用表达式作为属性名
obj['a'+'bc'] = 123;
let propKey = 'foo';
let obj = {
[propKey]: true,
['a' + 'bc']: 123
};
三.方法的name属性
- 函数的name属性,返回函数名。对象方法也是函数,因此也有name属性。
- bind方法创建的函数,name属性返回bound+原函数名
- Function构造函数创建的函数,name属性返回anonymous
四.属性的可枚举性和遍历
- 可枚举性
- 对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为。
- Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象。
- 描述对象的enumerable属性,称为“可枚举性”。
- 总的来说,操作中引入继承的属性会让问题复杂化,大多数时候,我们只关心对象自身的属性。所以尽量不要用for...in循环,而用Object.keys()代替。
- 属性的遍历
- ES6 一共有 5 种方法可以遍历对象的属性。
- for...in
- for...in循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)。
- Object.keys(obj)
- Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)的键名。
- Object.getOwnPropertyNames(obj)
- Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。
- Object.getOwnPropertySymbols(obj)
- Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有 Symbol 属性的键名。
- Reflect.ownKeys(obj)
- Reflect.ownKeys返回一个数组,包含对象自身的所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。
- super关键字
- 我们知道,this关键字总是指向函数所在的当前对象。
- ES6 又新增了另一个类似的关键字super,指向当前对象的原型对象。
- super关键字表示原型对象时,只能用在对象的方法之中,用在其他地方都会报错。
eg.报错,super用在属性里面
const obj={
foo:super.foo
}
eg.报错,super用在一个函数里面再赋值给foo属性
const obj={
foo:()=>super.foo
}
const obj={
foo:function(){
return super.foo
}
}
eg.正确,super用在对象的方法中
const obj={
find(){
return super.foo
}
}
- JavaScript引擎内部,super.foo等同于Object.getPrototypeOf(this).foo(属性)
- 或Object.getPrototypeOf(this).foo.call(this)(方法)。
五.对象的扩展运算符
- 解构赋值
eg.
let {x,y, ...z} ={x:1, y:2, a:3, b:4};
console.log(x,y, z); //==>1 2 {a: 3, b: 4}
eg.
new Vuex.Store({
state,
mutation,
types,
actions
})
- 对象的解构赋值用于从一个对象取值,相当于将目标对象自身的所有可遍历的、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面。
- 解构赋值要求等号右边是一个对象,如果等号右边是undefined或null,就会报错,因为它们无法转为对象。
let { ...z } = null; // 运行时错误let { ...z } = undefined; // 运行时错误
- 解构赋值必须是最后一个参数,否则会报错。
let { ...x, y, z } = someObject; // 句法错误let { x, ...y, ...z } = someObject; // 句法错误
- 解构赋值的拷贝是浅拷贝。
let obj = { a: { b: 1 } }; let { ...x } = obj; obj.a.b = 2; x.a.b // 2- 扩展运算符的解构赋值,不能复制继承自原型对象的属性。
const o = Object.create({ x: 1, y: 2 }); o.z = 3; let { x, ...newObj } = o; let { y, z } = newObj; x // 1 y // undefined z // 3- 变量x是单纯的解构赋值,变量y和z是扩展运算符的解构赋值,只能读取对象o自身的属性,所以变量z可以赋值成功,变量y取不到值。
- 变量声明语句时,如果使用解构赋值,扩展运算符后必须是一个变量名,而不能是一个解构赋值表达式,否则报错。 let { x, ...{ y, z } } = o; // SyntaxError: ... must be followed by an identifier in declaration contexts
- 解构赋值的一个用处,是扩展某个函数的参数,引入其他操作。
- 解构赋值要求等号右边是一个对象,如果等号右边是undefined或null,就会报错,因为它们无法转为对象。
function baseFunction({ a, b }) {
// ...
}
function wrapperFunction({ x, y, ...restConfig }) {
// 使用 x 和 y 参数进行操作
// 其余参数传给原始函数
return baseFunction(restConfig);
}
- 扩展运算符...
- 对象的扩展运算符用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。
let z = { a: 3, b: 4 };let n = { ...z };n // { a: 3, b: 4 }
- 数组是特殊的对象,所以对象的扩展运算符也可以用于数组。
let foo = { ...['a', 'b', 'c'] };foo // {0: "a", 1: "b", 2: "c"}
- 扩展运算符后面是一个空对象,则没有任何效果。
{...{}, a: 1} // { a: 1 }
- 扩展运算符后面不是对象,则会自动将其转为对象。
{...1} //等同于 {...Object(1)} 结果为{}- 扩展运算符后是整数1,会自动转为数值的包装对象Number{1}。
- 由于该对象没有自身属性,所以返回一个空对象。`
{...true} //等同于 {...Object(true)}结果为{}{...undefined} //等同于 {...Object(undefined)}结果为{}{...null} //等同于 {...Object(null)}结果为{}{...'hello'} //{0: "h", 1: "e", 2: "l", 3: "l", 4: "o"}- 扩展运算符后面是字符串,它会自动转成一个类似数组的对象
- 对象的扩展运算符用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。
六.链判断运算符
- 编程时如果读取对象内的某个属性,往往需要先判断该对象是否存在。
- eg.要读取message.body.user.firstName
const firstName = (message
&& message.body
&& message.body.user
&& message.body.user.firstName) || 'default';
- 或者使用三元运算符?:判断对象是否存在
const fooInput = myForm.querySelector('input[name=foo]')const fooValue = fooInput ? fooInput.value : undefined;
- 这样层层判断很麻烦,因此引入“链判断运算符”(?.)简化写法。
const firstName = messsage?.body?.user?.firstName || 'default';const fooValue = myForm.querySelector('input[name=foo]')?.value;
- 使用?.运算符,直接在链式调用的时候判断,左侧的对象是否为null或undefined。
- 如果是的,就不再往下运算而是返回undefined。
- 链判断运算符的用法
obj?.prop //对象属性obj?.[expr] //对象属性func?.(...args) //函数或对象方法的调用a?.b //等同于 a == null ? undefined : a.ba?.[x] //等同于 a == null ? undefined : a[x]a?.b() //等同于 a == null ? undefined : a.b()a?.() //等同于 a == null ? undefined : a()- eg.判断对象方法是否存在,如果存在就立即执行。
iterator.return?.() - eg.对于可能没有实现的方法,这个运算符很有用。
if (myForm.checkValidity?.() === false) { return;//表单校验失败 }- 老式浏览器的表单可能没有checkValidity()方法,这时?.运算符就会返回undefined,
- if语句就变成了undefined === false,所以就会跳过下面的代码。
- 短路机制
a?.[++x] //等同于 a == null ? undefined : a[++x]- 如果a为真,右侧表达式就不会进行递增运算。
- delete运算符
delete a?.b //等同于 a == null ? undefined : delete a.b- 如果a为null或undefined,会直接返回undefined不会进行delete运算。
- 括号的影响
(a?.b).c //等同于 (a == null ? undefined : a.b).c- 如果属性链有圆括号,链判断运算符对圆括号外部没有影响,只对圆括号内部有影响。
- 不管a对象是否存在,圆括号后面的.c总是会执行。
- 报错场合
- 以下写法是禁止的,会报错。
// 构造函数
new a?.()
new a?.b()
// 链判断运算符的右侧有模板字符串
a?.`{b}`
a?.b`{c}`
// 链判断运算符的左侧是 super
super?.()
super?.foo
// 链运算符用于赋值运算符左侧
a?.b = c
- 右侧不得为十进制数值
- 允许foo?.3:0被解析成foo ? .3 : 0也就是说,
- 那个小数点会归属于后面的十进制数字,形成一个小数。
七.Null判断运算符
- 设置属性默认值时有用到。
const animationDuration = response.settings.animationDuration || 300;const showSplashScreen = response.settings.showSplashScreen || true;- 通过||设置默认值时,我们想要,只要属性值为null或undefined,默认值就会生效,
- 但是属性的值如果为空字符串或false或0,默认值也会生效。
- 为了避免这种情况,引入了Null判断运算符(??)。
- 它的行为类似||,但只有运算符左侧的值为null或undefined时,才会返回右侧的值。
- eg.判断函数参数是否赋值
function Component(props){
const enable = props.enabled ?? true;
//等同于
const {
enabled: enable = true,
} = props;
}
- 运算符优先级:和&&,||一起使用时必须用括号表明优先级,否则会报错。
八.对象的方法
- Object.is() 用来比较两个值是否严格相等,是(同值相等)算法等同于===
- Object.assign() 用来复制对象,合并对象参数
- let 新对象 = Object.assign(目标对象,source1,source2,)
- eg.合并对象参数的属性相同时会覆盖
let json = {a:1},json2 = {b:2, a:2},json3 = {c:3}; let obj = Object.assign({}, json, json2,json3); console.log(obj);//==>{a: 2, b: 2, c: 3}- eg.复制对象
let arr = [1,3,5]; let arr2 = Object.assign([], arr); * eg.为对象添加属性 class Point { constructor(x, y) { Object.assign(this, {x, y}); } }- eg.为对象添加方法
Object.assign(SomeClass.prototype, { someMethod(arg1, arg2) { ··· }, anotherMethod() { ··· } }); // 等同于下面的写法 SomeClass.prototype.someMethod = function (arg1, arg2) { ··· }; SomeClass.prototype.anotherMethod = function () { ··· };- eg.克隆对象
function clone(origin) { return Object.assign({}, origin); }- eg.合并多个对象
- 将多个对象合并到某个对象。
const merge = (target, ...sources) => Object.assign(target, ...sources);- 如果希望合并后返回一个新对象,可以改写上面函数,对一个空对象合并。
const merge = (...sources) => Object.assign({}, ...sources);- eg.为属性指定默认值
const DEFAULTS = { logLevel: 0, outputFormat: 'html' }; function processContent(options) { options = Object.assign({}, DEFAULTS, options); console.log(options); // ... } - 可以用for...of循环遍历数组,它们都返回一个遍历器对象
- Object.keys() 是对键名的遍历,返回一个数组
- Object.values() 是对键值的遍历,返回一个数组
- Object.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" var obj1 = {"name":"lucas","age":22}; console.log(Object.keys(obj1)) //["name", "age"] - __proto__属性,Object.setPrototypeOf(),Object.getPrototypeOf()
- __proto__属性 用来设置一个对象的prototype对象
- Object.setPrototypeOf(object, prototype) 用于读取一个对象的原型对象。
- Object.getPrototypeOf(obj);
- Object.fromEntries()
- Object.entries()的逆操作,用于将一个键值对数组转为对象。
- 适合将 Map 结构转为对象。
const entries = new Map([ ['foo', 'bar'], ['baz', 42] ]); Object.fromEntries(entries)// { foo: "bar", baz: 42 }
第7章 Symbol数据类型
- mbol 意思是全局标记
- 如何用Symbol构建对象的Key,并调用和赋值?
var jspang = Symbol();
var obj={ [jspang]:'技术胖' }
console.log(obj[jspang]);//==>技术胖
obj[jspang]='web';
console.log(obj[jspang]);//==>web
- Symbol对象元素的保护作用?
- 象中有多个属性值,但是循环输出时并不希望全部输出,那我们就可以使用Symbol进行保护。
- 进行保护的写法:
var obj={name:'jspang',skill:'web',age:18};
for (let item in obj){
console.log(obj[item]);;//==>jspang web 18
}
- 我不想别人知道我的年龄,这时候我就可以使用Symbol来进行循环保护。
let obj={name:'jspang',skill:'web'};
let age=Symbol();
obj[age]=18;
for (let item in obj){
console.log(obj[item]);//==>jspang web
}
console.log(obj);//==>{name: "jspang", skill: "web", Symbol(): 18}
第8章 Set和Map数据结构
一.Set & WeakSet数据结构
- Set的数据结构是以数组的形式构建的。
let setArr = new Set(['jspang','技术胖','web','jspang']);
console.log(setArr);//==>Set {"jspang", "技术胖", "web"}
- Set和Array的区别?
- Set不允许内部有重复的值,如果有只显示一个相当于去重。
- 虽然Set很像数组但它不是数组。
- Set 追加用.add()
setArr.add('前端职场'); - Set 删除某一个用.delete()
setArr.delete('前端职场'); - Set 查找用.has()
setArr.has('jspang'); - Set 删除全部用.clear()
setArr.clear(); - Set 获取Set值的数量用size属性
setArr.size - forEach循环
left setArr = new Set(['jspang','技术胖','web','jspang']);
setArr.forEach( (value) => console.log(value) );
- WeakSet的声明
let weakObj=new WeakSet();
let obj={a:'jspang',b:'技术胖'}
weakObj.add(obj);
console.log(weakObj);
- 如果你直接在new 的时候就放入值将报错。
- WeakSet里边的值也是不允许重复的。
二.Map数据结构
- Map是一种灵活,简单的适合一对一查找的数据结构。
- Map可以看成是一个特殊的键值对,但Map的key可以设成数组,值也可以设成字符串,让它不规律对应起来。
- 当然也可key字符串,value是对象。我们调换一下位置,依然是符合map的数据结构规范的。
let json = {
name:'jspang',
skill:'web'
}
console.log(json.name);//==>jspang
var map=new Map();
map.set(json,'iam');
map.set('jspang',json);
console.log(map);//==>输出结果如下
Map(2) {{…} => "iam", "jspang" => {…}}
[[Entries]]
0: {Object => "iam"}
key: {name: "jspang", skill: "web"}
value: "iam"
1: {"jspang" => Object}
key: "jspang"
value: {name: "jspang", skill: "web"}
size: 2
__proto__: Map
- Map 取值用get
map.get(json); - Map 删除某一个用.delete()
map.delete(json); - Map 查找用.has()
map.has('jspang'); - Map 删除全部用.clear()
map.clear(); - Map 获取Set值的数量用size属性
console.log( map.size );
第9章 Proxy预处理
- Proxy是用来做什么的?
- 钩子函数:在运行函数前初始化一些数据,在改变对象值后做一些善后处理。这些都算钩子函数。
- Proxy的存在就可以让我们给函数加上这样的钩子函数。
- 你也可以理解为在执行方法前预处理一些代码。
- 你也可以理解为它是函数或者对象的生命周期。
- 声明Proxy
- new Proxy({},{});
- 是两个花括号,第一个花括号就相当于我们方法的主体。
- 第二个花括号就是Proxy代理处理区域,相当于我们写钩子函数的地方。
- get属性
- get属性是在你得到某对象属性值时预处理的方法,他接受三个参数
- target:得到的目标值
- key:目标的key值,相当于对象的属性
- property:这个不太常用,用法还在研究中,还请大神指教。
var pro = new Proxy({
add: function (val) { return val + 10; },
name: 'I am Jspang'
}, {
get:function(target,key,property){
console.log('come in Get');//==>先输出,相当于在方法调用前的钩子函数。
return target[key];
}
});
console.log(pro.name)
- set属性
- set属性是值你要改变Proxy属性值时,进行的预先处理。它接收四个参数。
- target:目标值。
- key:目标的Key值。
- value:要改变的值。
- receiver:改变前的原始值。
- 使用get,set
var pro = new Proxy({
add: function (val) { return val + 10; },
name: 'I am Jspang'
}, {
get:function(target,key,property){
console.log('come in Get');//==>第一个输出,相当于在方法调用前的钩子函数。
return target[key];
},
set:function(target,key,value,receiver){
console.log(`setting ${key} = ${value}`);//==>第三个输出 setting name = 技术胖
return target[key] = value;
}
});
console.log(pro.name);//==>第二个输出
pro.name='技术胖';
console.log(pro.name);//==>第四个输出
- apply的使用
- apply的作用是调用内部的方法,它使用在方法体是一个匿名函数时。
第10章 Promise对象
Promise从语法上说就是一个对象,它可以获取异步操作的消息。用来解决异步回调问题。
ES6规定,Promise对象是一个构造函数,用来生成Promsie实例。
- 创建一个Promise实例
let promise = new Promise(function(resolve,reject){
if(/*异步调用成功*/){
resolve(value)
}else{
reject(error)
}
})
- Promise实例生成后,指定成功和失败状态的回调函数
写法一
promise.then(res=>{
//success成功的方法
},err=>{
//error失败的方法
})
写法二
promise.then(res=>{
//success成功的方法
})
promise.catch(err=>{
//error失败的方法
})
- Promise.resolve('a') 将现有的东西转成一个promise对象是成功状态
等价于
new Promise(resolve=>{
resolve('aaa');
})
- Promise.all([p1,p2,p3]) 把Promise打包扔到一个数组里面,打包完还是一个Promise对象
- 必须确保所有的Promise对象都是resolve状态才可用all()
- Promise.race([p1,p2,p3]) 只要有一个成功,就返回
- 同时处理没有关联的多个异步操作
let p1 = Promise.resolve('aaaa');
let p2 = Promise.resolve('bbbb');
let p3 = Promise.resolve('cccc');
Promise.all([p1,p2,p3]).then(res=>{
//console.log(res);
let [res1, res2, res3] = res;
console.log(res1, res2, res3);
})
同时处理有关联的多个异步操作
let status = 1;
let userLogin = (resolve,reject)=>{
if(status == 1){
resolve({data:'登录成功',msg:'xxx',token:'xxx'});
}else{
reject('失败了');
}
}
let getUserInfo = (resolve,reject)=>{
if(status == 1){
resolve({data:'获取用户信息成功',msg:'xxx',token:'xxx'});
}else{
reject('失败了');
}
}
new Promise(userLogin).then(res=>{
console.log('登录成功');
return new Promise(getUserInfo);
}).then(res=>{
console.log('获取用户信息成功');
})
第11章 Class(类)的使用
- class是一个类也是一个函数
- class中不要用逗号和分号
- class没有预解析,没有提升功能,实例化一定要放在下面
function Person(){ // ES5
this.name = 'andy';
}
Person.prototype.showA = function(){
return this.name;
}
var p1 = new Person();
p1.showA();
class Person(){ // ES6
constructor(name){//构造函数,调用new会自动执行
this.name = name;
}
showA(){
return this.name;
}
}
let p1 = new Person('andy');
p1.showA();
- class表达式
const Person = class{
constructor(name){this.name=name;}
}
- 属性表达式
class Person(){
constructor(){}
[aaa+bbb](){
return '使用属性表达式'
}
}
let aaa ='strive',let bbb ='method';
let p1 = new Person();
alert( pl[aaa+bbb]() ); //==>'使用属性表达式'
alert( p1.strivemethod() ); //==>'使用属性表达式'
- 矫正this
- fn.call(this指向谁,args1,args2...)
- fn.apply(this指向谁,[args1,args2...])
- fn.bind()
class Person{
constructor(){
this.name = 'Strive';
this.showName = this.showName.bind(this); //矫正this
}
showName(){
console.log('this:', this);
return `名字为: ${this.name}`;
}
showA(){ return 'showA'}
}
let p1 = new Person();
let {showName } = p1,{showA } = p1;
console.log(showA(),showName());
//==>showA
//==>this: Person {name: "Strive", showName: ƒ}
//==>名字为: Strive
- 静态方法:直接通过类来调用
class Person(){
constructor(){}
showName(){}
static aaa(){}
}
let p1 = new Person();
alert( p1.showName() );
alert( Person.aaa() );
- 继承
function Person(name){ //ES5
this.name = name;
}
Person.prototype.showName = function(){}
function Student(name,skill){
Person.call(this.name); //继承父类属性
this.skill = skill; //定义子类属性
}
Student.prototype = new Person();//继承父类方法
class Person(){ // ES6 //父类
constructor(name){
this.name = name
this.age = age
}
showName(){}
}
class Student extends Person{//子类
constructor(name,age,skill){
super(name,age)
this.skill = skill //this要必须放到super的后面
}
showA(){
super.showName() //执行父类的方法
}
}
let stu = new Student('andy','teacher');
第12章 模块化操作
- 需要放到服务器环境
- as用来设置别名
- 如何定义模块?(都放到js文件中)
- export导出东西需要加{}
- export default导出东西不需要加
let a = 1, b = 2,c = 3;
export {a,b};
export default c;
export const d = 1;
export {
name as n,
showA //showA是定义的方法
}
- 如何使用模块?
<script type="module"></script> - 导入全部文件
import './1.js'; //(./)当前路径,不加就会报错import * as login from './1.js';
- 导入文件中的模块
import {name as n,age} from './1.js';import c,{a,b} from './1.js';
- import导入时要把export default默认的放前面,export的放后面
- import有提升效果,会自动提升到顶部,首先执行
- import无论你引入几次,模块只会导入一次
- import支持相对路径和绝对路径
- import类似node中的require,可以动态引入;import的返回值是一个Promise对象; 动态引入可以按需加载,可以写在if中,路径也可以是动态的;
function config(sign){
switch(sign){
case 1:
return './modules/1.js';
break;
case 2:
return './modules/2.js';
break;
}
}
let sign = 1;
import(config(sign)).then(res=>{
});
- 模块化默认就是严格模式'use strict'
- 模块中顶层的this指向undefined,即不要在顶层代码中使用this