福建网站建建设,网站建设四川推来客网站系统,首页设计说明,提高网站速度Decorator装饰器
针对属性 / 方法的装饰器
// decorator 外部可以包装一个函数#xff0c;函数可以带参数function Decorator (type) {/*** 这里是真正的decorator* description: 装饰的对象的描述对象* target:装饰的属性所述类的原型#xff0c;不是实例后的类。如果装饰…Decorator装饰器
针对属性 / 方法的装饰器
// decorator 外部可以包装一个函数函数可以带参数function Decorator (type) {/*** 这里是真正的decorator* description: 装饰的对象的描述对象* target:装饰的属性所述类的原型不是实例后的类。如果装饰的是Animal的某个属性,这个target就是Animal.prototype* name 装饰的属性的key*/return function (target, name, desciptor) {// 因为babel的缘故 通过value并不能获取值以此可以获取实例化的时候此属性的默认值let v desciptor.initializer desciptor.initializer.call(this)// 返回一个新的描述对象或者直接修改desciptor也可以return {enumerable: true, //可以遍历configurable: true, //可以删除get: function () {return v},set: function (c) {v c}}}}// 上面的不能和业界商用的Decorator混用function Check (type) {return function (target, name, desciptor) {let v desciptor.initializer desciptor.initializer.call(this)// 将属性名字以及需要的类型的对应关系记录到类的原型上if (!target.constructor._checkers_) {// 将这个隐藏属性定义成no enumerable,遍历的时候是取不到的Object.defineProperty(target.constructor, _checkers_, {value: {},enumerable: false,writable: true,configurable: true})}target.constructor._checkers_[name] {type: type}return desciptor}}// 装饰函数的第一个参数 target 是包装属性所属的类的原型prototype// 也就是把对应关系挂载到了开发定义的子类上。vue中使用Decorator ts开发一定对vue-property-decorator不会感到陌生这个插件提供了许多装饰器 在methods里面的方法上面使用装饰器这时候装饰器的target对应的是methods。 可以在生命周期钩子函数上面使用装饰器这时候target对应的是整个组件对象。 import {log,confirmation} from ./testmethods: {log()name() {console.log(获取数据);},confirmation(此操作将永久删除文件,是否继续?)deleteFile(data){//删除文件操作}},mounted () {this.name()}test.js import {MessageBox}from element-ui
export function confirmation(message){return function(target,name,descriptor){let oldValue descriptor.valuedescriptor.value function(...args){MessageBox.confirm(message,提示).then(oldValue.bind(this,...args)).catch((){})}return descriptor}
}
export function log(){/*** description: * param {*} target 对应methods* param {*} name 对应属性方法的名称* param {*} descriptor 对应属性方法的修饰符* return {*}*/ return function(target,name,descriptor){console.log(target,name,descriptor);// 获取实例化的时候此属性的默认值const fn descriptor.value/* 重写 */descriptor.value function(...rest){console.log(调用${name}方法打印的);fn.call(this,...rest)}}}Iterator 迭代器 目的是为不同的数据结构提供统一的数据访问机制 主要为for of 服务的 当for of执行的时候循环过程中会自动调用这个对象上的迭代器方法,依次执行迭代器对象的next方法并把next返回结果赋值给for of的变量从而得到具体的值 迭代器对象返回此对象的方法叫做迭代器方法 此对象有一个next方法 每次调用next方法都会返回一个结果值 这个结果值是一个object 包含两个属性value和done value表示具体的返回值 done是布尔类型的表示集合是否遍历完成或者后续还有可用数据没有可用返回true, 否则返回false 内部会维护一个指针 用来指向当前集合的位置 每调用一次next方法 指针都会向后移动一个位置可以想象成数组的索引 代码实现 getInterator(list){var i 0;return {next:function(){var done (ilist.length);var value !done ? list[i]:undefinedreturn {done:done,value:value}}}}var it this.getInterator([a,b,c])console.log(it.next());// {done: false, value: a}console.log(it.next());//{done: false, value: b}console.log(it.next());//{done: false, value: c}console.log(it.next());//{done: true, value: undefined}可迭代对象 Symbol.Iterator是一个表达式 返回Symbol的Iterator属性 这是一个预定好的类型为Symbol的特殊值 ES6规定只要在对象上部署了Iterator接口具体实现为给对象添加Symbol.Iterator属性此属性指向一个迭代器方法这个迭代器会返回一个迭代器对象。 而部署了这个属性并且实现迭代器方法 返回的对象就是迭代器对象此时这个对象就是可迭代的 可以被for for遍历 实现一个可迭代对象 getIterator(){let iteratorObj {items:[100,200,300],[Symbol.iterator]: function(){var self thisvar i 0return {next:function(){var done (iself.items.length)var value !done?self.items[i]:undefinedreturn {done:done,value:value}}}}}for(var item of iteratorObj){console.log(item); //100 200 300}}//上面的对象就是可迭代对象可以被for of遍历this.getIterator()for of 中断 如果for of 循环提前退出则会自动调用return方法,需要注意的是return 方法必须有返回值且返回值必须是一个object var arr [100, 200, 300]arr[Symbol.iterator] function () {var self thisvar i 0return {next: function () {var done i self.lengthvar value !done ? self[i] : undefinedreturn {done: done,value: value}},return (){console.log(提前退出);return { //必须返回一个对象done:true}}}}for (var o of arr) {if(o 200){break;}console.log(o) // 100 提前退出}除了for of 会调用对象的Iterator, 结构赋值也会 var str 123let [a,b]strconsole.log(a,b); // 1 2let map new Map()map.set(q,1)map.set(a,2)map.set(b,3)let [c,d] mapconsole.log(c,d); //[q, 1] [a, 2]因为普通对象不是可迭代对象。 自定义的可迭代对象进行解构赋值 var interatorObj {items: [橙, 红, 白],[Symbol.iterator]: function () {let i 0let self thisreturn {next: function () {let done i self.items.lengthlet value !done ? self.items[i] : undefinedreturn {done: done,value: value}}}}}let [a,b] interatorObjconsole.log(a,b); //橙 红解构赋值的变量的值就是迭代器对象next方法的返回值 且是按顺序返回 扩展运算符 // 扩展运算符的执行…也会默认调用它的Symbol.iterator方法可以将当前迭代对象转换为数组 */\* 字符串 \*/*var str 1234console.log([...str]);*//【1,2,3,4】转换成数组**/\* map对象\*/*var map new Map([[1,2],[3,4]])[...map]*//[[1,2],[3,4]]**/\* set对象 \*/*var set new Set([1,2,3])[...set] *//[1,2,3]*使用普通对象是不可以转换为数组的 var obj {name:zhang}[...obj] *//报错*作为数据源
作为一些数据的数据源 比如某些参数是数组的API方法都会默认的调用自身的迭代器
例如 map set Array.from等
为了证明先把一个数组的默认迭代器给覆盖掉
var arr [100,200,300]arr[Symbol.iterator]function(){var self thisvar i 0;return {next:function(){var done (iself.length)var value !done?self[i]:undefinedreturn {done:done,value:value 前端}}}}
用for of for(var o of arr){console.log(o);//100前端 200前端 300前端}生成set对象
var set new Set(arr)set*//Set(3) {100前端, 200前端, 300前端}*
调用Array.from方法 Array.from(arr) *//[100前端, 200前端, 300前端]**yield**关键字
*yield**后面跟的是一个可遍历的结构执行时也会调用迭代器函数
let foo function*(){ *yield* 1; *yield** [1,2,3]; *yield* 5; }判断对象是否可迭代
既然可迭代对象的规则必须在对象上部署Symbol.iterator属性,那么就可以依次来判断是否为可迭代对象然后就知道是否能使用for of 取值了
function isIterable(*object*){ *return* typeof *object*[Symbol.iterator] function } console.log(isIterable(asdd));*//true* console.log(isIterable([1,2,3]));*//true* console.log(isIterable(new Map()));*//true* console.log(isIterable(new Set()));*//true* console.log(isIterable(new WeakMap()));*//false* console.log(isIterable(new WeakSet()));*//falseGenerate生成器* let obj { *[Symbol.iterator](Symbol.iterator){ *yield* hello; *yield* world; } } *for*(let x of obj){ console.log(x); } *//hello worldGenerator
Generator函数是ES6提供的一种异步编程解决方案语法行为与传统函数完全不同
Generator函数将javascript异步编程带入了一个全新的阶段
声明
与函数声明类似不同的是function关键字与函数名之间有一个星号以及函数体内部使用yield表达式定义不同的内部状态(yield在英语里的意思就是产出)
function* foo(){yield result1yield result2yield result3}const gen foo()console.log(gen.next().value);console.log(gen.next().value);console.log(gen.next().value);vue中使用 textMy () {const gen this.foo()console.log(gen.next().value);console.log(gen.next().value);console.log(gen.next().value);},* foo(){yield result1yield result2yield result3} 执行Generator会返回一个对象而不是像普通函数返回return 后面的值
调用指针next方法会从函数的头部或上一次停下来的位置开始执行直到遇到下一个yield表达式或者return语句暂停也就是执行yeild这一行
value就是执行yield后面的值 done表示函数是否执行完毕
console.log(g.next()); *//{value: 7, done: false}* console.log(g1.next()); *//{value: 2, done: false}* console.log(g.next());*//{value: undefined, done: true} 因为最后一行 return y 被执行完成 所以done为true
调用Generator函数后该函数并不执行返回的也不是函数运行结果 而是一个指向内部状态的指针对象,也就是遍历器对象Iterator Object.必须调用遍历器对象的next方法
使得指针移向下一个状态 function* fetch(){ *yield* ajax(aaa) *yield* ajax(bbb) *yield* ajax(ccc) } let gen fetch() let res1 gen.next() *//{value:aaa,done:false}* let res2 gen.next() *//{value:bbb,done:false}* let res3 gen.next() *//{value:ccc,done:false}* let res4 gen.next() *//{value:undefined,done:true} //done为true表示执行结果由于Generator函数返回的是遍历器对象 只有调用next方法才会遍历下一个内部状态 所以其实提供了一种可以暂停执行的函数。yield表达式就是暂停标志
遍历器对象的next方法运行逻辑
1 遇到yield表达式就暂停执行后面的操作并将紧跟在yield后面的表达式的值作为返回对象的value属性值
2. 如果下次调用next方法时再继续往下执行直到遇到下一个yield表达式
3 如果没有再遇到新的yiled表达式 就一直运行到函数结束
4 如果该函数没有return 语句 则返回对象的value属性值就是underfined
yiled表达式本身没有返回值 或者说总返回underfined next 方法可以带一个参数该参数就会被当做上一个yield表达式的返回值
一个函数可以有多个yield但是只能有一个return
Proxy
- Proxy用于创建对象的代理用于监听对象的相关操作。代理对象可以监听我们对原对象的操作
- 在实例化Proxy对象时第二个参数传入的是捕获器集合我们在其对象内定义一个get捕获器用于监听获取对象值的操作
- objProxy 对象的拦截器中set捕获器用于监听对象的某个属性被设置时触发
// 定义一个普通的对象const obj {name:_isLand}// 代理obj这个对象 并传入get捕获器const objProxy new Proxy(obj,{// get捕获器get:function(target,key){console.log(触发对象捕获${key});return target[key]},set:function(target,key,val){console.log(捕获到对象设置${key},新值为${val});return target[key] val}})objProxy.name liming// 通过代理对象操作obj对象console.log(objProxy.name);//触发对象捕获name _isLand// 捕获到对象获取name属性值的操作如果不想这个属性被设定成这个值你可以抛出异常告诉开发者这个值不能设定 set:function(target,key,val){if(keyage typeof val number){return target[key] val}else{throw new TypeError(该属性的值必须是number类型)}}监听对象是否调用了getPrototypeof操作使用getPrototypeOf捕获器 getPrototypeOf:function(){console.log(监听到对象的protptypeOf操作);}Proxy一共有十三个捕获器用于我们对 对象或者函数的方法调用的监听 this指向的问题
Proxy对象可以对我们的目标对象进行访问,但没有做任何拦截时也不能保证与目标行为一致因为目标对象 内部的this会自动改变为Proxy代理对象 let obj {name:lili,foo:function(){return this objProxy}}let objProxy new Proxy(obj,{})console.log(obj.foo()); //falseconsole.log(objProxy.foo()); //true对象监听
在vue2中的watchApi是使用的Es5的Object.defineProperty(对象属性描述符)对对象监听,将一个对象进行遍历
并设定setter。getter方法进行监听和拦截
const obj {name: liki,age: 12}Object.keys(obj).forEach(key {let val obj[key]Object.defineProperty(obj, key, {get: function () {console.log(触发get)return val},set: function (newVal) {console.log(触发set)val newVal}})})obj.age 100console.log(obj.name);Object.defineProperty的设计初衷并不是为了去拦截拦截一个对象中的属性且他也实现不了更加丰富的操作例如删除、添加属性等操作
- 所以在Es6中新增了Proxy,对象用于监听Object,Function的操作。
- 在Vue3框架中的响应式原理也是用到了Proxy对象进行对属性的监听操作
proxy是一个代理对象 他可以代理我们对原目标的操作。相比Object.defineProperty方法 Proxy监听的事件更加方便
Reflect隐射对象 Reflect是一个对象翻译过来就是反射的意思,它提供了很多操作Javascript对象的方法是为弥补Object中对象的一些缺陷。且所有的属性和方法都是静态的 最初js的一些内部方法都是放在Object这个对象上的比如getPrototye,defineProperty等API in、delete操作符都放在了Object对象上。但是Object作为一个构造函数,这些方法都放在Object上并不合适 所以ES6之后的内部新方法都在Reflect对象中。Refelect不是构造函数 使用Reflect对象操作Object对象 Reflect对象让我们操作Object对象不再是通过点语法 而是变成了函数行为 所以 获取对象属性可以使用Reflect.get方法将对象的属性赋值使用Reflect.set方法 const obj {name:lili,age:12}/* 获取对象的属性值 */console.log(obj.name);console.log(Reflect.get(obj,name));// 对对象的属性赋值obj.name lalaReflect.set(obj,name,lala)console.log(Reflect.get(obj,name));// 判断一个对象中是否有该属性console.log(name in obj);console.log(Reflect.has(obj,name));Reflect中的方法 在返回值方面Reflect对象中的方法设计的更加合理 比如defineProperty方法如果没有将属性设置
成功在Reflect中会返回boolean值而Object对象中如果没有定义成功则会抛出TypeError
Reflect搭配Proxy
Reflect对象中的方法和Proxy对象中的方法对应的Proxy对象中的方法也能在Reflect对象中调用
通常我们将Reflect对象搭配Proxy一起使用
在下面Proxy对象中get,set捕获器多了一个recerive参数 这是这两个捕获器特有的 代表的是当前的代理对象
当Proxy和Reflect搭配使用时 Proxy对象会拦截对应的操作 后者完成对应的操作如果传入receiver,那么Reflect.get属性
会触发Proxy.definProperty捕获器。如果没有传入receive参数 则不会触发defineProperty捕获器 let obj {name:lili}const objProxy new Proxy(obj,{get:function(target,key,receiver){return Reflect.get(target,key,receiver)},set:function(target,key,val,receiver){return Reflect.set(target,key,val,receiver)},defineProperty:function(target,key,attr){console.log(defineProperty,target,key,attr); //defineProperty {name: lili} name {value: ppp}return Reflect.defineProperty(target,key,attr)}})objProxy.name pppconsole.log(objProxy.name);//传入在我们获取代理对象中的name属性时,当Reflect有receive总结
- Reflect 对象中集合了javascript内部方法
- 操作Object对象的方式变成了函数行为
- Reflect 对象中的方法返回结果更加合理
ES6中的class
在ES6之前js语法是不支持类的导致面向对象编程无法直接使用而是通过function来实现模拟类随着js的更新ES6中出现了Class,
用于定义类
class Animal {}const Animal class {}类的构造函数
每一个类都可以有一个自己的构造函数这个名称是固定的construtor,当我们通过new 调用一个类时这个类就会调用自己的constructor方法(构造函数)
- 它用于创建对象时给类传递一些参数
- 每一个类只能有一个构造函数
- 通过 new 调用一个类时 会调用构造函数 执行如下操作
1 在内存中开辟一块新的空间用于创建新的对象
2 这个对象内部的_proto_属性会被赋值为该类的prototype属性
3 构造函数内部的this指向创建出来的新对象
4 执行构造函数内部的代码
5 如果函数没有返回值 则返回this class Animal {constructor(name){this.name name}}var a new Animal(ABC)console.log(a); //Animal {name: ABC}
class 中定义的constructor这个就是构造方法
而this代表的是实例对象
这个class 可以看作是构造函数的另一种写法 因为它和它的构造函数的相等的,即是 类本身指向构造函数
console.log(Animal Animal.prototype.constructor); *//true*所以其实类上的所有方法都会放到prototype属性上
类中的属性
实例属性
实例的属性必须定义在类的方法中
class Animal {constructor(name,height,weight){this.name namethis.height heightthis.weight weight}}静态属性
当我们把一个属性赋值给类本身而不是赋值给它的prototype,这样子的属性被称之为静态属性static
静态属性直接通过类来访问 无需在实例中访问
class Foo{static name liLI}console.log(Foo.name);私有属性
私有属性只能在类中读取写入不能通过外部引用私有字段 class Person{#age;constructor(age,name){this.#age agethis.name name}}var a new Person(17,lili)console.log(a); //Person {name: lili}console.log(a.name);//liliconsole.log(a.age);//undefinedconsole.log(a.#age);// Private field #age must be declared in an enclosing class
console.log(Object.getOwnPropertyDescriptors(a));
通过getOwnPropertyDescriptors获取属性同样获取不到 {name: {value: _island,writable: true,enumerable: true,configurable: true}}在ES6之前我们定义类中的方法是类中的原型上定义的 防止类中的方法重复在多个对象中
function Animal{} Animal.prototype.eating function(){ console.log(this.name eating); } 在Es6中定义类中的方法更简洁 直接在类中定义即可 这样的写法优雅可读性也强 class Animal{eating(){ console.log(this.name eating); } }静态方法
静态方法是与静态属性是一样的 在方法前面加上stati关键字声明,之后调用这个方法时不需要通过类的实例来调用可以直接通过类名来调用它
class Animal{ static creatName(*name*){ *return* *name* } } var a2 Animal.creatName(lll) console.log(a2); *//lll私有方法
在面向对象中 私有方法是一个常见需求 ES6中没有提供可以通过某个方法实现它 class Foo{ _getBoodType(){ *return* 0 } } 需要注意的是通过下划线开头通常我们会局限它是一个私有方法 但是在类的外部还是可以正常调用到这个方法的
类的继承 extends关键字用于扩展子类,创建一个类作为另一个类的子类 它会将父类中的属性和方法一起继承到子类的减少子类中重复的业务代码 这比之前在ES5中修改原型链实现继承的方法可读性要强的多而且写法更简洁 extends的使用 class Animal{} dog继承Animal类 class dog extends Animal{} 继承类的属性和方法
定义一个dog类 通过extends关键字继承Animal类的属性和方法在子类的construtor方法中 使用super关键字在子类中它上市必须存在的 否则新建实例会抛出异常。这是因为子类的this对象是继承父类的this对象 如果不调用super方法 子类就得不到this对象
class Animal { constructor (*name*) { this.name *name* } eating () { console.log(this.name eating) } } class dog extends Animal{ constructor(*name*,*legs*){ super(*name*) this.legs *legs* } speaking(){ console.log(this.name speaking); } } var wang new dog(tom,4) wang.speaking() wang.eating() console.log(wang.name);Super super关键字用于访问调用一个对象的父对象上的函数 super指的是超级顶级父类的意思 在子类的构造函数中使用this或者返回默认对象之前必须先使用super调用父类的构造函数 子类的construtor方法先调用了super方法它代表了父类的构造函数也就是我们把参数传递进去之后其实它调用了父类的构造函数
class Animal{ constructor(*name*) } class dog{ constructor(*name*,*type*,*weight*){} super(*name*) this.type type this.weight weight } 使用super调用父类的方法 class Animal{ constructor(*name*){ this.name name } eating(){ console.log(this.name eating); } } *// dog继承Animal类* class dog extends Animal{ constructor(*name*,*lengs*){ super(*name*) this.lengs *lengs* } speaking(){super.eating() console.log(this.name speaking); } } var a new dog(旺财,4) a.speaking() *//旺财eating 旺财speakingGetter和Setter
在类内部可以使用get和set关键字对应某个属性设置存值和取值函数拦截属性的存取行为
class Animal{ constructor(){ this._age 3 } get age(){ console.log(get); *return* this._age } set age(*val*){ console.log(set); this._age val } } var a new Animal() console.log(a.age); a.age 4 console.log(a.age);关于class扩展
严格模式 在类和模块的内部 默认是严格模式 所以不需要使用use strict指定运行模式。只要你的代码写在类或模块之中就只有严格模式可用 name属性 Es6中的类只是Es5构造函数的一层包装,所以函数的许多属性都被class继承了包括name属性 class Animal{}console.log(Animal.name); 变量提升 class不存在变量提升这与Es5中实现类是不同的 function关键字会存在变量提升 new Foo() * ReferenceError*class Foo{}在Es6之后在定义类以及它内部的属性方法还有继承操作的语法变得很简洁易懂 class 是一个语法糖 其内部还是通过Es5中的语法实现的。且有些浏览器不支持class语法 我们可以通过babel来进行转换