当前位置: 首页 > news >正文

网页制作与网站建设实战教程视频教程百度热搜关键词排行榜

网页制作与网站建设实战教程视频教程,百度热搜关键词排行榜,lens wordpress,广州官方网站建设前言 今日早读文章由携程机票研发部前端工程师Sky.Gu授权分享。 正文从这开始~~ 引言 Vue中的数据绑定 Vue作为前端框架的三驾马车之一,在众多前端项目中具有极其重要的作用。 Vue中具有一个重要的功能点——“数据绑定”。使用者无需关…

前言

今日早读文章由携程机票研发部前端工程师@Sky.Gu授权分享。

正文从这开始~~

引言

Vue中的数据绑定

Vue作为前端框架的三驾马车之一,在众多前端项目中具有极其重要的作用。

Vue中具有一个重要的功能点——“数据绑定”。使用者无需关心数据是如何绑定到dom上面,只需要关注数据本身即可。

那实现其功能的原理是什么?

阅读官方文档(v2.0),我们会发现:

把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。

关键字是Object.defineProperty,在MDN文档找到说明如下:

Object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。

我们再仔细查询在MDN文档的说明会发现,Object.defineProperty()存在两种属性描述符:

数据描述符(简略介绍)

  • configurable:数据可改变

  • enumerable:可枚举

  • value:属性值

  • writable:可读写

 

存取描述符

 

  • get:一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。

  • set:一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。

 

至此也就引出了getter/setter。

ES5 getter/setter

让我们通过一个例子来测试一下。

首先,建立一个英雄(Hero)对象并赋予其一些属性:

let hero = {name:'赵云',hp: 100,sp: 100}

然后使用Object.defineProperty()来对其具有的一些属性进行修改,并且在控制台输出修改的内容:

Object.defineProperty(hero, 'hp', {set (val) {console.log(`Set hp to ${val}`);return val;}})hero.hp = '200';// --> Set hp to 200

假若把console.log(‘Set hp to ${val}’) 改为 element.innerHTML = val,是不是就可以实现数据绑定了?

那让我们再修改一下英雄的属性,假设英雄拥有很多装备:

let hero = {name:'赵云',hp: 100,sp: 100,equipment:['马','长枪']}

我们把“佩剑”添加到英雄的装备中,并且输出在控制台:

Object.defineProperty(hero.equipment, 'push', {value () {this[this.length] = arguments[0];}})hero.equipment.push('佩剑');
console.log(hero.equipment);// --> [ '马','长枪', '佩剑' ]

由此,我们可以看到对象的属性变化可以依靠get()和set()方法去追踪和改变;但对于数组则需要使用value()方法实现。

显然,这不是最好的方法,那有没有更好的方法可以简化对象或数组属性变化呢?

答案是肯定的。

概念

Proxy

Proxy意思为“代理”,即在访问对象之前建立一道“拦截”,任何访问该对象的操作之前都会通过这道“拦截”,即执行Proxy里面定义的方法。

基本用法:

let pro = new Proxy(target,handler);
  • new Proxy()表示生成一个Proxy实例

  • target参数表示所要拦截的目标对象

  • handler参数也是一个对象,用来定制拦截行为。

handler

Proxy支持13种拦截行为(handle),针对解决上一节的问题,简单介绍下其中2种拦截行为,get与set。

get

get(target, propKey, receiver)

用于拦截某个属性的读取操作,可以接受三个参数:

  • target:目标对象

  • propKey:属性名

  • receiver(可选):proxy 实例本身(严格地说,是操作行为所针对的对象)

set

set(target, propKey, value, receiver)

用于拦截某个属性的赋值操作,可以接受四个参数:

  • target:目标对象

  • propKey:属性名

  • value:属性值

  • receiver(可选):Proxy 实例本身

实例

在解决上一节问题之前,先一同看几个实例。

实例1

let hero = {name: "赵云",age: 25}let handler = {}let heroProxy = new Proxy(hero, handler);console.log(heroProxy.name);// --> 赵云
heroProxy.name = "黄忠";
console.log(heroProxy.name);// --> 黄忠

解析:

创建hero对象为所要拦截的对象;

拦截操作对象handler为空,未对拦截对象设定拦截方法;

该情况下heroProxy直接指向原对象target,访问heroProxy等同于访问target,所以结果为target中的结果。

实例2

let hero = {name: "赵云",age: 25}let handler = {get: (hero, name, ) => {const heroName =`英雄名是${hero.name}`;return heroName;},set:(hero,name,value)=>{console.log(`${hero.name} change to ${value}`);hero[name] = value;return true;}}let heroProxy = new Proxy(hero, handler);console.log(heroProxy.name);
heroProxy.name = '黄忠';
console.log(heroProxy.name);// --> 英雄名是赵云// --> 赵云 change to 黄忠// --> 英雄名是黄忠

解析:

创建hero对象为所要拦截的对象;

handler对象为拦截对象后执行的操作,这里get方法为读取操作,即用户想要读取heroProxy中的属性时执行的拦截操作。

最后创建一个Proxy实例,当读取heroProxy中的属性时,结果打印出来的总是“黄忠”字符串。

实例3

Proxy也可以作为其他对象的原型对象使用。

let hero = {name: "赵云",age: 25}let handler = {get: (hero, name, ) => {const heroName =`英雄名是${hero.name}`;return heroName;},set:(hero,name,value)=>{console.log(`${hero.name} change to ${value}`);hero[name] = value;return true;}}let heroProxy = new Proxy(hero, handler);let obj = Object.create(heroProxy);console.log(obj.name);
obj.name = '黄忠';
console.log(obj.name);// --> 英雄名是赵云// --> 赵云 change to 黄忠// --> 英雄名是黄忠

解析:

在实例2的基础上,将heroProxy作为obj的原型对象使用。

虽然obj本身没有name这个属性,但是根据原型链,会在heroProxy上读取到name属性,之后会执行相对应的拦截操作。

解决数据绑定问题

在我们对Proxy有了一定了解后,可以尝试解决上一节的问题。

首先,还是定义一个英雄:

let hero = {name:'赵云',hp: 100,sp: 100,equipment:['马','长枪']}

接着,定义一个handler:

let handler = {set(target, property, value) {console.log(`hero's ${property} change to ${value}`);target[property] = value;return true;}}

然后,修改英雄的hp值

let heroProxy = new Proxy(hero, handler);
heroProxy.hp = 200;// --> hero's hp change to 200
console.log(hero.hp);// --> 200

最后,同样把“佩剑”添加到英雄的装备中

let heroProxy = new Proxy(hero.equipment, handler);
heroProxy.push('佩剑');// --> hero's 2 change to 佩剑// --> hero's length change to 3
console.log(hero.equipment);// --> ["马", "长枪", "佩剑"]

可以发现,heroProxy.push(‘佩剑’);触发了两次set,原因是push即修改了hero.equipment的内容,又修改了hero.equipment的length。

Reflect

在了解了Proxy之后,细心的我们一定发现,若需要在Proxy内部调用对象的默认行为,该如何实现?

Reflect正是ES6 为了操作对象而提供的新 API。

基本特点

只要Proxy对象具有的代理方法,Reflect对象全部具有,以静态方法的形式存在。这些方法能够执行默认行为,无论Proxy怎么修改默认行为,总是可以通过Reflect对应的方法获取默认行为。

修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false。

让Object操作都变成函数行为。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为。

静态方法

Reflect对象一共有 13 个静态方法(匹配Proxy的13种拦截行为)。

  • Reflect.apply(target, thisArg, args)

  • Reflect.construct(target, args)

  • Reflect.get(target, name, receiver)

  • Reflect.set(target, name, value, receiver)

  • Reflect.defineProperty(target, name, desc)

  • Reflect.deleteProperty(target, name)

  • Reflect.has(target, name)

  • Reflect.ownKeys(target)

  • Reflect.isExtensible(target)

  • Reflect.preventExtensions(target)

  • Reflect.getOwnPropertyDescriptor(target, name)

  • Reflect.getPrototypeOf(target)

  • Reflect.setPrototypeOf(target, prototype)

 

大部分与Object对象的同名方法的作用都是相同的,而且它与Proxy对象的方法是一一对应的。

实例

下面通过3个实例来对比Object对象方法与Reflect对象方法。

实例1

//Object对象方法try {Object.defineProperty(target, name, property);} catch (e) {console.log("error");}//Reflect对象方法if (Reflect(target, name, property)) {console.log("success");} else {console.log("error")}

解析:

由于Reflect(target, name, property)返回的是boolean,代码语义性更好。

实例2

let hero = {name: '赵云',hp: 100,sp: 100,equipment: ['马', '长枪']}//Object对象方法
console.log('name' in hero);// --> true//Reflect对象方法
console.log(Reflect.has(hero,'name'));// --> true

解析:

Object操作是命令式,而Reflect让它们变成了函数行为

实例3

let hero = {name: '赵云',hp: 100,sp: 100,equipment: ['马', '长枪']}let handler = {get(target, name, receiver) {if (name === "name") {console.log("success");} else {console.log("failure");}return Reflect.get(target, name, receiver);}}let heroProxy = new Proxy(hero, handler);
console.log(heroProxy.name);// --> success// --> 赵云

解析:

Reflect对象的操作和Proxy对象的操作一一对应,在Proxy的拦截操作中,可以直接利用Reflect对象直接获取Proxy的默认值。

结合实践

掌握了Proxy与Reflect的知识点后,除了解决文章开头的数据绑定问题之外,挑选日常编码中容易遇见的两种情况进行编码实践。

观察者模式

观察者模式(Observer mode)指的是函数自动观察数据对象,一旦数据有变化,函数就会自动执行。

let hero = {name: '赵云',hp: 100,sp: 100,equipment: ['马', '长枪']}const handler = {set(target, key, value, receiver) {//内部调用对应的 Reflect 方法const result = Reflect.set(target, key, value, receiver);//执行观察者队列observableArray.forEach(item => item());return result;}}//初始化Proxy对象,设置拦截操作const createProxy = (obj) => new Proxy(obj, handler);//初始化观察者队列const observableArray = new Set();const heroProxy = createProxy(hero);//将监听函数加入队列
observableArray.add(() => {console.log(heroProxy.name);});heroProxy.name = "黄忠";// --> 黄忠

该实例在set拦截行为中加入了监听函数的执行,使每一次值的改变均能被监听。

对象多重继承

实现对象间的单继承,比如obj2继承obj1,可以使用Object.setPrototypeOf方法,但是没法实现多继承。

const people = {name: 'people',run() {console.log('people.run:', this.name);}};const powerMan = {name: 'powerMan',run() {console.log('powerMan.run:', this.name);},fight() {console.log('powerMan.fight:', this.name);}};const handler = {get(target, name, receiver) {if (Reflect.has(target, name)) {return Reflect.get(target, name, receiver);}else {for (let P of target[Symbol.for('[[Prototype]]')]) {if (Reflect.has(P, name)) {return Reflect.get(P, name, receiver);}}}}};const hero = new Proxy({name: 'hero',strike() {this.run();this.fight();}}, handler);hero[Symbol.for('[[Prototype]]')] = [people, powerMan];
hero.strike();// --> people.run:hero// --> powerMan.fight:hero

用了一个自定义的属性Symbol.for(“[[Prototype]]”)来表示要继承的多个父对象。

然后用Proxy来拦截所有hero中的get请求,使用Reflect.has方法检查hero中是否存在相应的属性或者方法。

如果存在,则直接转发;如果不存在,则遍历父对象列表,在父对象中逐个检查是否存在相应的属性或者方法。

若存在则调用。若不存在,则相当于get返回undefined。

关于本文
作者:@Sky.Gu
原文:https://zhuanlan.zhihu.com/p/60126477

http://www.lbrq.cn/news/2671795.html

相关文章:

  • 海口手机建站模板优就业seo课程学多久
  • 如何学习网站建设店铺推广怎么做
  • 阿里万网怎么做网站百度识图网页版
  • 网站怎么申请微信支付接口免费外链网站
  • dedecms修改网站教程百度广告客服电话
  • 合肥专业网站制作团队西地那非片的功能主治和副作用
  • 外贸高端网站设计网络营销包括
  • 颛桥做网站seo公司怎样找客户
  • 什么网站做设计可以赚钱网站排名优化公司哪家好
  • 有哪些网站可以做外贸批发seo怎样优化网站
  • 工程建设科学技术奖申报网站免费seo课程
  • 做b2c网站需要多少钱神童预言新冠2023结束
  • 网站维护升级页面外贸新手怎样用谷歌找客户
  • 新冠政策最新20条长沙seo网站推广
  • 宅男做网站宁波优化网站排名软件
  • wordpress 网站提速百度快照和广告的区别
  • 网站如何优化推广新东方教育机构官网
  • 如何制作简单网站百度灰色关键词代发
  • 安阳百度网站制作多少钱淘宝app官方下载
  • 西安微信网站开发站长工具seo综合查询分析
  • 做外贸一般上哪些网站泰安网站seo推广
  • 跨境电商网站建设主管岗位职责中央电视台一套广告价目表
  • [ 1500元做网站_验收满意再付款! 推推蛙品牌策划
  • 做兼职的网站都有哪些企业管理培训课程网课
  • 高端网站建设天软科技关键词优化按天计费
  • 银川住房和城乡建设部网站济南网站优化排名推广
  • 哪些做任务可以赚钱的网站网站怎么推广
  • 网站空间香港今天的重要新闻
  • 祥云网站建设百度关键词排名怎么做
  • 在网上找做设计是什么网站企业网站排名优化公司
  • @ContextConfiguration
  • 查看 php 可用版本
  • docker compose和docker-compose命令的区别
  • JAVA+AI简化开发操作
  • [激光原理与应用-223]:机械 - 机加厂加工机械需要2D还是3D图?
  • Leetcode-19. 删除链表的倒数第 N 个结点