ES6 get() set() bind()
在TS中有的就不在提及
TS的那篇
const命令
禁止变量指向别的对象字符串的几个方法
- string.includes(“mystr”,num)返回bool值,表示从string下标为num的位置开始找,是否找到了mystr
- string.startsWith(“mystr”,num)返回bool值,表示从string下标为num的位置开始往后的字符串,是否在mystr为首部
- string.endsWith(“mystr”,num)返回bool值,表示在string的前num个元素中,是否mystr在尾部
- string.repeat(n)返回string的n此重复
- string.padStart/End(length,
mystr
)用mystr头部/尾部讲补全string至length的长度 - string.replace(
a
,b
)用b置换第一个找到的a,全部置换则是string.replace(/a/g,b
)
二进制和八进制
二进制前缀0b,八进制前缀0onumber的几个方法
isFinite(),isNaN(),parseInt(),parseFloat(),isInteger(),isSafeInteger()指数运算符**,例 2**3=8
箭头表达式需要注意的点
- 箭头表达式函数内的this,指的是所在对象的this,不指向使用时的this
class mycla{ name; constructor(){ document.write(this.name); } }
这里的this指的是mycla,不是document
- 不能用argument
- 不能作为generator函数
- 箭头表达式函数内的this,指的是所在对象的this,不指向使用时的this
双冒号运算符
foo::bar();
讲bar中的this指定为foo
尾调用/递归
意思是,在函数最后一步调用其他函数(两者只能在严格模式下才能起作用)
调用/递归的时候因为要返回上一个函数,栈要很深,占位置。使用尾调用/递归,因为不需要再返回上一个函数,栈的深度永远只有1function factorial(n, total = 1) { if (n === 1) return total; return factorial(n - 1, n * total); }
阶乘的尾递归写法
有关数组
- …的运用
- 将一个数组分为若干个单参数
function add(a,b){ return a+b; } let numbers=[1,2]; add(...numbers);
- 将一个数组分为若干个单参数
numbers被拆成1和2传入add
- 复制数组
const a1=[1,2]; const a2=a1; const a3=[...a1]; /*const[...a3]=a1*/
a2是a1的引用,改变a1,a2也会变,a3才是a1的克隆(a3的两种复制写法都可以)
合并数组
const a1=[1,2]; const a2=[3,4]; const a3=[...a1,...a2];
将字符串转化成真正的数组
const a1=[...`hello`];//a1为["h","e","l","l","o"]
实际上,任何 Iterator 接口的对象,都可以用…运算符转为真正的数组
总的来说,…就是可以将数组/类似数组的对象拆分成单个元素,因此可以实现复制和合并数组的功能- Array.from()可以将所有具有length的对象转化成数组。同时,Array.from()还可以接受第二个参数,用来对每个元素处理
let a1=Array.from([1,2,3],x=>x**2); /*a1=[1,4,9]*/
简单来说,数组的每个元素会被传入这个函数,然后等于这个函数的返回值
Array.of(),返回参数值组成的数组
let a1=Array.of(1,2,3); /*a1=[1,2,3]*/
copyWithin数组内部复制(会影响原数组)
copyWithin(target, start = 0, end = this.length)
target 从这个位置开始覆盖数据
start 从这个位置开始读取数据
end 到这个位置结束读取数据(不读取这个位置的数据)[1, 2, 3, 4, 5].copyWithin(0, 3); // [4, 5, 3, 4, 5]
find和findIndex函数 找出并返回数组里第一个符合条件的元素
let num=[1, 5, 10, 15].find(value => value > 9); /*num=10*/
find()的参数为判断函数,判断函数返回一个布尔值,为true时就停止回调,回调完都找不到就返回undefine
findIndex()函数差不多,就是如果找不到符合条件的元素就返回-1
它们都可以接受第二个参数,如果有第二个参数,它们里面的this指针指向第二个参数的对象- fill()数组填充
['a', 'b', 'c'].fill(7, 1, 2); /*['a',7,'c']*/
代码意思:讲这个数组用7填充,从下标为1开始,到下标为2结束填充(不包括2)
- …的运用
entries(),keys(),values()用于遍历数组或对象
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"
keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历
合并对象Object.assign(obj1,obj2)
可以用来复制对象
但这是浅拷贝,而且会有同名属性替换的特性
也可以利用这个特性,为对象提供默认值function processContent(options) { options = Object.assign({}, DEFAULTS, options); console.log(options); } /*如果传入的options和DEFAULTS有相同属性,就会替换,否则options就会采用DEFAULTS的属性值*/
为对象添加/修改属性或方法
也可以对数组进行操作,数组是特殊对象,属性为0,1,2···n-1
reduce函数
function func3(val){ let func1=x=>x+1; let func2=x=>x*2; let funcs=[func1,func2]; funcs.reduce((a,b)=>b(a),val); } func3(5);//12 /*这里的val代表起始值,reduce里的a代表当前总的值,reduce会把funcs的值依次放入b,通过某些处理后返回一个值给a,作为新的当前总值*/ //val可以不写,默认0
* 新增的类型Symbol()
ts
let s1 = Symbol();
let s2 = Symbol();
s1 === s2 // false
每一个Symbol都是独特的,它可以接受一个参数,作为对这个变量的说明
Symbol变量可以通过String()或Boolean()转换成想相应的值
当它作为对象属性时,不能用点运算符(a.sym)提取,只能用,且在定义属性时,也必须把变量名放在[]中
* 返回对象所有键名Reflect.ownKeys(obj) 这是一个数组
* 希望使用同一个Symbol,可以用Symbol.for(`id`)
```ts
let s1 = Symbol.for('foo');
let s2 = Symbol.for('foo');
s1 === s2 // true
```
Symbol.for(`id`)会把这个id的Symbol登记起来,如果要创建一个已登记的Symbol,就返回登记过的那个Symbol,两个变量是一致的
*Symbol.keyFor方法返回一个已登记的 Symbol 类型值的key*
Set结构
set类似数组,区别是set里面没有重复元素const set = new Set([1, 2, 3, 4, 4]); [...set]; // [1, 2, 3, 4]
这意味着,Set函数可以用来剔除数组重复元素
let a1=[1,2,3,3,4,4]; a1=new Set(a1); /*a1=[1,2,3,4]*/
它有以下常用四个函数
add(value):添加某个值,返回 Set 结构本身。
delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
has(value):返回一个布尔值,表示该值是否为Set的成员。
clear():清除所有成员,没有返回值。
同时,它也可以通过Array.from()变成数组
此外还有一个WeakSet,跟Set差不多,区别就是WeakSet是一个类,里面只可以加有iterable的对象Map结构
一般来说,对象的键只能是字符串,但是Map“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键- 两种构造方法
const set = new Set([ ['foo', 1], ['bar', 2] ]); const m1 = new Map(set); /*也可以直接 const m1= new Map([['foo',1],['bar',2]]); */ m1.get('foo') // 1 const m2 = new Map([['baz', 3]]); const m3 = new Map(m2); m3.get('baz') // 3
用键值对数组或者Map对象来创建新的Map对象
通过set()和get()函数建立/获取键值对
set(元素名,元素值):
get(元素名);
此外,它还有size,has,delete,clear等函数切换成数组[…Map]
Map 转为对象
如果所有 Map 的键都是字符串,它可以无损地转为对象。function strMapToObj(strMap) { let obj = Object.create(null); for (let [k,v] of strMap) { obj[k] = v; } return obj; } const myMap = new Map() .set('yes', true) .set('no', false); strMapToObj(myMap) // { yes: true, no: false }
如果有非字符串的键名,那么这个键名会被转成字符串,再作为对象的键名。
- 对象转为 Map
function objToStrMap(obj) { let strMap = new Map(); for (let k of Object.keys(obj)) { strMap.set(k, obj[k]); } return strMap; } objToStrMap({yes: true, no: false}) // Map {"yes" => true, "no" => false}
- 两种构造方法
对象的遍历属性Symbol.iterator,作用是返回给next()或循环调用,Symbol.iterator()函数返回一个对象,这个对象里有一个可以获取下一个键值对的函数(在外部给一个对象设置新属性,可以用object.prototype.name=value)
自带Symbol.iterator的一些对象:Array,Map,Set,String,TypedArray,函数的arguments对象,NodeList对象
Symbol.iterator是一个Symbol属性的变量所以得这样定义let obj = { data: [ 'hello', 'world' ], [Symbol.iterator]() { const self = this; let index = 0; return { next() { if (index < self.data.length) { return { value: self.data[index++], done: false/*done:false代表这个元素存在*/ }; } else { return { value: undefined, done: true }; } } }; } };
有时候也可以直接调用已有的Symbol.iterator
let iterable = { 0: 'a', 1: 'b', 2: 'c', length: 3, [Symbol.iterator]: Array.prototype[Symbol.iterator] }; for (let item of iterable) { console.log(item); // 'a', 'b', 'c' }
如果要用数组的iterator,就得用这样的类似数组的对象(需要存在数值键名,即0,1,2之类的,且要有length属性)
实际上[…str],就是调用了str的[Symbol.iterator]的next(),我们可以改写Symbol.iterator,来改变[…str]的值其实,也可以和generator结合起来
let myIterable = { [Symbol.iterator]: function* () { yield 1; yield 2; yield 3; } } [...myIterable] // [1, 2, 3]
这样写是最简便的
Generator函数的扩展
- generator.next()可以带参数,意思是改变上一个yield的值
function* foo(x) { var y = 2 * (yield (x + 1)); var z = yield (y / 3); return (x + y + z); } var a = foo(5); a.next() /* Object{value:6, done:false}*/ a.next() /* Object{value:NaN, done:false}*/ a.next() /* Object{value:NaN, done:true}*/ var b = foo(5); b.next() /* { value:6, done:false }*/ b.next(12) /* { value:8, done:false }*/ b.next(13) /* { value:42, done:true }*/
函数中,y是不能正确等于2 * (yield (x + 1))的,函数的执行是,首先返回x+1,然后y=2*传入的参数,如果没有传入参数,yield所在的地方就会等于NaN
利用generator写Symbol.iterator的高级用法
function* objectEntries() { let propKeys = Object.keys(this); for (let propKey of propKeys) { yield [propKey, this[propKey]]; } } let jane = { first: 'Jane', last: 'Doe' }; jane[Symbol.iterator] = objectEntries; for (let [key, value] of jane) { console.log(`${key}: ${value}`); } // first: Jane // last: Doe
throw方法
Generator 函数返回的遍历器对象,都有一个throw方法,可以在函数体外抛出错误,然后在 Generator 函数体内捕获。var g = function* () { try { yield; } catch (e) { console.log('内部捕获', e); } }; var i = g(); i.next(); try { i.throw('a'); i.throw('b'); } catch (e) { console.log('外部捕获', e); } // 内部捕获 a // 外部捕获 b
几个要注意的地方
- 函数内部的catch只能catch一次,所以第二次被外部catch到了
- 另外,throw方法抛出的错误要被内部捕获,前提是必须至少执行过一次next方法
- 只要 Generator 函数内部部署了try…catch代码块,throw方法被捕获以后,会附带执行下一条yield表达式。也就是说,会附带执行一次next方法
- 函数中如果使用了throw new Error(‘generator broke!’)之类的命令,函数就只结束运行
除了我们手动调用throw方法或者throw一些内容,当函数自己运行出现错误的时候,也会自动throw错误
return方法
可以直接终止函数运行,返回的value为return里面的参数(不写就是undefined),如果 Generator 函数内部有try…finally代码块,那么return方法会推迟到finally代码块执行完再执行。function* numbers () { yield 1; try { yield 2; yield 3; } finally { yield 4; yield 5; } yield 6; } var g = numbers(); g.next() /* { value: 1, done: false }*/ g.next() /* { value: 2, done: false }*/ g.return(7) /* { value: 4, done: false }*/ g.next() /* { value: 5, done: false }*/ g.next() /* { value: 7, done: true }*/
在Generator函数中不能直接调用别的Generator函数,要用yield*
function* foo() { yield 'a'; yield 'b'; } function* bar() { yield 'x'; yield* foo(); yield 'y'; /*x,a,b,y*/ }
yield* 的意思是遍历这个对象,也可以yield* [“a”, “b”, “c”], yield* ‘hello’
- generator.next()可以带参数,意思是改变上一个yield的值
绑定this指针
obj.function.call(对象),这里的对象就是this指针指向的对象,当然也可以是函数或者其他类的getter和setter
class MyClass { constructor() { } get prop() { return 'getter'; } set prop(value) { console.log('setter: '+value); } } let inst = new MyClass(); inst.prop = 123; // setter: 123 inst.prop // 'getter'
通过子类实例对象给父类添加属性或方法proto
class B extends A { } let B1=new B; B1._proto_._proto_.printName=()=>{console.log("Hi");}; B1._proto_===B.prototype;/*true*/ B.prototype.__proto__ === A.prototype;/*true*/
因此,也可以用B.prototype.proto.printName()=>{console.log(“Hi”);}
当然,更加可以直接用A.ptototype.printName()=>{console.log(“Hi”);}修饰器
- 类的修饰
普通例子:
@testable class MyTestableClass { } function testable(target) {/*传入的target就是MyTestableClass这个类*/ target.isTestable = true; } MyTestableClass.isTestable // true
高端一点的例子:
/* mixins.js*/ export function mixins(...list) { return function (target) { Object.assign(target.prototype, ...list)/*Object.assign(target,a,b,c,d...)函数能把后面若干个类的成员复制到target里面*/ } } /* main.js*/ import { mixins } from './mixins' const Foo = { foo() { console.log('foo') } }; @mixins(Foo) class MyClass {} let obj = new MyClass(); obj.foo() /* 'foo' 因为上面是用的target.prototype,因此这里可以用实例调用*/
- 类的方法的修饰
class Person { @nonenumerable get kidCount() { return this.children.length; } } function nonenumerable(target, name, descriptor) { descriptor.enumerable = false; return descriptor; }
修饰类方法的修饰器函数模板如上,三个参数
修饰器不能装饰函数
因为有函数提升,如果真的要用修饰器,可以采用高阶函数的形式直接执行第三方模块修饰器
这里举个例子core-decorators.js
在调用之前,我们需要先写这句import { 修饰器名 } from 'core-decorators';
介绍三个比较有用的修饰器
autobind修饰器使得方法中的this对象,绑定原始对象。
readonly修饰器使得属性或方法不可写。
override修饰器检查子类的方法,是否正确覆盖了父类的同名方法,如果不正确会报错。
当然,我们也可以利用修饰器会先执行的特性,在修饰器中写一些事件。这样在调用类的方法时,就是先执行相应事件- Trait
我们除了可以像上面那样使用prototype给类加属性,也可以直接使用tarit,这里使用traits-decorator这个第三方模块作为例子
import { traits } from 'traits-decorator'; class TFoo { foo() { console.log('foo') } } const TBar = { bar() { console.log('bar') } }; @traits(TFoo, TBar) class MyClass { } let obj = new MyClass(); obj.foo() /* foo*/ obj.bar() /* bar*/
这里就直接给MyClass加入了TFoo和TBard类的方法
但是,注意一点,tarit不允许加入同名方法。这里我们可以给方法起个别名import { traits, alias } from 'traits-decorator'; class TFoo { foo() { console.log('foo') } } const TBar = { bar() { console.log('bar') }, foo() { console.log('foo') } }; @traits(TFoo, TBar::alias({foo: 'aliasFoo'})) class MyClass { } let obj = new MyClass(); obj.foo() /* foo*/ obj.aliasFoo() /* foo*/ obj.bar() /* bar*/
改函数名的一个函数alias,可以参考下(复习一下:双冒号可以用来绑定函数里面的this指针)
- 类的修饰