changes

changes 查看完整档案

上海编辑华东理工大学  |  计算机科学与技术 编辑  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 该用户太懒什么也没留下

个人动态

changes 提出了问题 · 11月25日

vscode插件开发,怎么取消json文件的鼠标hover提示

image.png

做了一个vscode插件,当鼠标停留在json文件内容上时,会有一个hover提示内容,但是这和vscode自带的鼠标hover提示有冲突。
不知道怎么把自带的hover提示去掉

关注 1 回答 0

changes 提出了问题 · 11月25日

vscode 插件,registerHoverProvider的时候,怎么关闭vscode自带的hover提示

image.png
image.png

功能是当鼠标悬停在json文件内容上的时候,给出当前单词的提示。
但是vscode对json文件有自带的hover提示,貌似会覆盖掉插件的hover内容。

如果是针对vue文件,则不会有这个问题。
不知道怎么关闭这个自带的对json文件的hover提示

关注 1 回答 0

changes 收藏了文章 · 2019-05-05

数码照片Exif - Orientation 自动修正解决方案

使用Canvas + exif-js自动修正数码照片

使用场景,在做朋友圈 H5 时,时常遇到需要用户拍照上传图片需求,但是在一些手机(iso)上拍出来的照片会出现奇怪的旋转角度来呈现。经过各种百度才发现相机拍出来的图片拥有很多属性,其中一项是Orientation ,用于记录拍摄时相机物理旋转角度,例如把相机倒过来Orientation3,顺时针竖起来Orientation6,逆时针竖起来Orientation8,正常模式Orientation1。根据这个属性我们可以使用Canvas来对图片重绘。

Orientation 示意图

sadas

Show Code


import EXIF from 'exif-js'; // 引入依赖插件

// 参数列表:img 对象,callback返回Base64图片编码,生成图片质量默认值0.9
export const FixImg = (img, callback, quality = 0.9) => {
    
  let Orientation, ctxWidth, ctxHeight, base64; // 定义所需变量

  EXIF.getData(img, function() {
    Orientation = EXIF.getTag(this, 'Orientation');
    ctxWidth = this.naturalWidth;
    ctxHeight = this.naturalHeight;
    
    console.log(Orientation, ctxWidth, ctxHeight);

    var canvas = document.createElement('canvas');
    var ctx = canvas.getContext('2d');

    canvas.width = ctxWidth;
    canvas.height = ctxHeight;
    if ([5, 6, 7, 8].includes(Orientation)) {
      canvas.width = ctxHeight;
      canvas.height = ctxWidth;
    }

    switch (Orientation) {
      case 2:
        ctx.transform(-1, 0, 0, 1, ctxWidth, 0);
        break;
      case 3:
        ctx.transform(-1, 0, 0, -1, ctxWidth, ctxHeight);
        break;
      case 4:
        ctx.transform(1, 0, 0, -1, 0, ctxHeight);
        break;
      case 5:
        ctx.transform(0, 1, 1, 0, 0, 0);
        break;
      case 6:
        ctx.transform(0, 1, -1, 0, ctxHeight, 0);
        break;
      case 7:
        ctx.transform(0, -1, -1, 0, ctxHeight, ctxWidth);
        break;
      case 8:
        ctx.transform(0, -1, 1, 0, 0, ctxWidth);
        break;
      default:
        ctx.transform(1, 0, 0, 1, 0, 0);
    }

    ctx.drawImage(img, 0, 0, ctxWidth, ctxHeight);
    
    // 默认输出jpeg,也可以读取原图片格式,最后输出原图格式,搜索关键词 :File.type
    base64 = canvas.toDataURL('image/jpeg', quality); 
    callback(base64);
  });
};

性感照骗,在线修复: http://peichenhu.cn/demo/awesome/#/Exif


相关补充:从图片 Exif 信息中取到 Orientation 后,就可以根据它来自动旋转图片了,canvas、filter 滤镜、vml、css3 都可以实现图片的旋转。
参考文章:https://imququ.com/post/how-t...

查看原文

changes 收藏了文章 · 2019-04-02

【Service Worker】生命周期那些事儿

文章来自 我的博客

生命周期是 Service Worker 中比较复杂的一部分,如果你不知道它什么时间将要做什么,以及它带来的好处,那么你可能会有一种感觉:就是它一直在和你较劲。如果理解它的工作机制,你就可以给用户提供完美的,无感知的更新体验。 这篇文章是 Chrome 团队最近总结的一片文章,配合例子讲述生命周期,让我们更容易理解,也解决了我之前开发中遇到的一些困惑,所以决定翻译出来。此处 阅读原文

目的

本文中介绍利用生命周期可以实现的功能大概有如下几点:

  • 实现缓存优先(offline-first)

  • 在不打断现有 SW 的情况下,准备好一个新的 SW

  • 让注册 SW 的页面同一时间只归属同一个 SW 控制

  • 确保你的网站只有一个版本在运行

最后一点尤其重要,一般情况下(没有 SW 的情况),用户浏览你的网站时可能先打开一个 tab,过了一会儿又打开了一个 tab,结果就是在同一时间,你的页面运行了两个版本,大部分时候,这样是没问题的,但是如果你使用了缓存,那么两个 tab 就要面临如何管理缓存的问题,如果处理不好,它可能会造成异常,严重的造成数据丢失。

用户非常不喜欢数据丢失,这会让他们非常忧桑。

第一个 Service Worker

  • install 事件是 SW 触发的第一个事件,并且仅触发一次。

  • installEvent.waitUntil()接收一个 Promise 参数,用它来表示 SW 安装的成功与否。

  • SW 在安装成功并激活之前,不会响应 fetchpush等事件。

  • 默认情况下,页面的请求(fetch)不会通过 SW,除非它本身是通过 SW 获取的,也就是说,在安装 SW 之后,需要刷新页面才能有效果。

  • clients.claim()可以改变这种默认行为。

举个例子:

<!DOCTYPE html>
3秒后将出现一张图片:
<script>
  navigator.serviceWorker.register('/sw.js')
    .then(reg => console.log('SW registered!', reg))
    .catch(err => console.log('Boo!', err));

  setTimeout(() => {
    const img = new Image();
    img.src = '/dog.svg';
    document.body.appendChild(img);
  }, 3000);
</script>

这里注册了一个 SW,并且在 3 秒后在页面上添加一个图片。

这是 SW 的代码:

self.addEventListener('install', event => {
  console.log('V1 installing…');

  // 这里缓存一个 cat.svg
  event.waitUntil(
    caches.open('static-v1').then(cache => cache.add('/cat.svg'))
  );
});

self.addEventListener('activate', event => {
  console.log('V1 now ready to handle fetches!');
});

self.addEventListener('fetch', event => {
  const url = new URL(event.request.url);

  //如果是同域并且请求的是 dog.svg 的话,那么返回 cat.svg
  if (url.origin == location.origin && url.pathname == '/dog.svg') {
    event.respondWith(caches.match('/cat.svg'));
  }
});
    

这里 SW 缓存了 cat.svg,并当请求 dog.svg 的时候返回它。然而,当你运行 这个例子 的时候,你会发现第一次出现一只狗,刷新后,猫才会出现。

作用域与控制

SW 的默认作用域为基于当前文件 URL 的 ./。意思就是如果你在//example.com/foo/bar.js里注册了一个 SW,那么它默认的作用域为 //example.com/foo/

我们把页面,workers,shared workers 叫做clients。SW 只能对作用域内的clients有效。一旦一个client被“控制”了,那么它的请求都会经过这个 SW。我们可以通过查看navigator.serviceWorker.controller是否为 null 来查看一个client是否被 SW 控制。

下载-解析-执行

当你调用.register()的时候,第一个 SW 被下载下来,这过程中如果下载,解析或者在初始化中有错误的话,那么 register 的Promise 会返回 reject,然后 SW 会被销毁。

Chrome 开发者工具会展示出来错误,在 Application 中的 Service Workers Tab:

alt

安装(install)

SW 首先会触发install,每个 SW 只会被触发一次,当你修改你的 SW 后,浏览器会认为这是一个新的 SW,从而会再触发这个新 SW 的install事件,在后面会详细说到。

install是在 SW 控制 clients之前处理缓存很好的时机。在 event.waitUntil()传入的 Promise 会让浏览器知道 SW 什么时候安装成功以及是否成功。

当 Promise reject 的时候,代表着安装失败,浏览器将这个 SW 废弃掉,不会控制任何 clients。

激活(Activate)

安装成功后并激活(activate)成功后,SW 就可以处理“功能性的事件“了,比如push,sync但这并不代表调用.register()的页面会立即生效。

第一次你请求 这个demo 的时候,虽然在 SW 被激活后很久才请求了dog.svg(因为这里等待了三秒),但 SW 也并没有处理这个请求,结果你看见了一只狗。当你第二次请求的时候,也就是刷新页面,这时请求被处理了,当前页面和图片都经过了 SW 的 fetch事件,所以你看见了一只猫。

clients.claim

你可以在activate事件中通过调用clients.claim()来让没被控制的 clients 受控。

比如 这个demo ,可能第一次你就会看见一只猫,这里我说“可能”,是因为这时时间敏感的,仅当 SW 激活并且clients.claim()被调用成功在图片请求之前的时候才可以。

所以,可想而知,当你用 SW 加载与正常请求不同资源的时候(比如上面的例子),那用clients.claim()可能会遇到一些问题,这时有些资源可能不会通过你的SW。

我见过很多人在代码中把clients.claim()当做了必选项,但我自己很少这样做,因为仅仅是第一次加载不会通过 SW,而且页面还是都会正常运行的。

更新 Service Worker

简单来说:

  • 触发更新的几种情况:

    • 第一次导航到作用域范围内页面的时候

    • 当在24小时内没有进行更新检测并且触发功能性时间如pushsync的时候

    • SW 的 URL 发生变化并调用.register()

  • 当 SW 代码发生变化,SW 会做更新(还将包括引入的脚本)

  • 更新后的 SW 会和原始的 SW 共同存在,并运行它的install

  • 如果新的 SW 不是成功状态,比如 404,解析失败,执行中报错或者在 install 过程中被 reject,它将会被废弃,之前版本的 SW 还是激活状态不变。

  • 一旦新 SW 安装成功,它会进入wait状态直到原始 SW 不控制任何 clients。

  • self.skipWaiting()可以阻止等待,让新 SW 安装成功后立即激活。

<div style="max-width:600px;">

<iframe id="iframe-2" onload="iframeLoaded('iframe-2')" style="width:100%;border:none;" data-original="https://www.zhengqingxin.com/static/demo/5-lifecycle/iframe2.html">
</iframe>

</div>

下面我们来举个 SW 更新的例子,这是一个将猫变成马的故事:

const expectedCaches = ['static-v2'];

self.addEventListener('install', event => {
  console.log('V2 installing…');

  // 将 horse.svg 缓存在新的缓存 static-v2 中
  event.waitUntil(
    caches.open('static-v2').then(cache => cache.add('/horse.svg'))
  );
});

self.addEventListener('activate', event => {
  // 删除额外的缓存,static-v1 将被删掉
  event.waitUntil(
    caches.keys().then(keys => Promise.all(
      keys.map(key => {
        if (!expectedCaches.includes(key)) {
          return caches.delete(key);
        }
      })
    )).then(() => {
      console.log('V2 now ready to handle fetches!');
    })
  );
});

self.addEventListener('fetch', event => {
  const url = new URL(event.request.url);

  //如果是同域并且请求的是 dog.svg 的话,那么返回 horse.svg

  if (url.origin == location.origin && url.pathname == '/dog.svg') {
    event.respondWith(caches.match('/horse.svg'));
  }
});

如果你打开 这个 demo ,你会看到一只喵,原因是这样的...

install

注意这里我们将缓存从 static-v1 换到了 static-v2,这代表了我用了一个新的缓存空间覆盖了之前 SW 正在使用的缓存。

这里新建了一块缓存的做法类似于原生 app 中将每块资源打包到一块指定的执行空间的做法,有时候结合实际情况,你也可以不这么做。

Waiting

一旦新 SW 安装成功,它会进入wait状态直到原始 SW 不控制任何 clients。这个状态是waiting,这也是浏览器确保在同一时间只有一个版本的 SW 运行的机制。

如果你再次打开 这个 demo ,你还是会看到一只喵,因为新的 SW 还是没有被激活,在开发者工具里你依然看到它是 waiting 状态。

alt

尽管这个例子中你仅打开了一个 tab,但刷新页面并没有用,这是由于浏览器本身的机制,当你刷新的时候,当前页面不会离开,直到收到了一个响应头,而且即使这样,如果响应中包含Content-Disposition的话,当前页面还是不会离开。由于这个时间上的重叠,在刷新的时候当前的 SW 总是控制了一个 client。

为了让 SW 更新,你需要把所有用原始 SW 的页面 tab 关闭或者跳转走,这时你再访问 这个 demo ,你就会看到了一匹野马。

这种机制类似于 Chrome 本身的更新机制,Chrome 在后台更新,只有当你重启浏览器的时候才会生效,在这期间你不会被打扰,可以继续使用当前版本。然而,这样可能会使我们开发者比较痛苦,好在开发者工具帮我们解决了这个事情,后面会说到。

Activate

Activate 在旧的 SW 离开时会被触发,这时新的 SW 可以控制 clients。这时候你可以做一些在老 SW 运行时不能做的事情,比如清理缓存。

在上面的例子中,之前保留的缓存,在activate时间执行的时候被清理掉。

这里最好不要更新以前的版本,而是直接分配新的缓存空间。

如果你在event.waitUntil()中传入了一个 Promise,SW 将会缓存住功能性事件(fetch,push,sync等等),直到 Promise 返回 resolve 的时候再触发,也就是说,当你的fetch事件被触发的时候,SW 已经被完全激活了。

cache storage API 和 localStorage,IndexedDB 一样是“同域”的。如果你在一个父域下运行多个网站,比如 yourname.github.io/myapp,这就要小心你不要把别的网站的缓存删掉了。避免这个问题,你可以将 cache 的 key 设的具有唯一性,比如 myapp-static-v1 并且约束不要碰不以 myapp- 开头的缓存。

skipWaiting

waiting 意在让你的网站同一时间只有一个 SW 在运行,但如果你不想要这样的话,你可以通过调用self.skipWaiting()来让新 SW 立即激活。

这么做会让你的新 SW 踢掉旧的,然后当它变为 waiting 状态时立即激活,注意这里不会跳过 installing,只会跳过 waiting。

在 waiting 之前或者之后调用skipWaiting()都可以,一般情况我们在 install 事件中调用:

self.addEventListener('install', event => {
  self.skipWaiting();

  event.waitUntil(
    // caching etc
  );
});

这个例子中,你可能直接可以看到一只奶牛,和clients.claim()一样,这是一场赛跑,仅当你的新 SW 安装,激活等早于你请求图片时,奶牛才会出现。

skipWaiting()意味着新 SW 控制了之前用旧 SW 获取的页面,也就是说你的页面有一部分资源是通过旧 SW 获取,剩下一部分是通过新 SW 获取的,如果这样做会给你带来麻烦,那就不要用skipWaiting(),这点我们应该根据具体情况评估。

手动更新

像我之前说的,当页面刷新或者执行功能性事件时,浏览器会自动检查更新,其实我们也可以手动的来触发更新:

navigator.serviceWorker.register('/sw.js').then(reg => {
  // sometime later…
  reg.update();
});

如果你希望你的用户访问页面很长时间而且不用刷新,那么你可以每个一段时间调用一次update()

避免改变 SW 的 URL

如果你看过我的文章缓存最佳实践,你可能会考虑给每个 SW 不同的 URL。千万不要这么做!在 SW 中这么做是“最差实践”,要在原地址上修改 SW。

举个例子来说明为什么:

1.index.html注册了sw-v1.js作为SW。

2.sw-v1.jsindex.html做了缓存,也就是缓存优先(offline-first)。

3.你更新了index.html重新注册了在新地址的 SW sw-v2.js.

如果你像上面那么做,用户永远也拿不到sw-v2.js,因为index.htmlsw-v1.js缓存中,这样的话,如果你想更新为sw-v2.js,还需要更改原来的sw-v1.js

在上面的 demo 里,我给每个 SW 用了不同的 URL,这只是为了做演示,不要在生产环境中这么做。

让开发更简单

SW 的生命周期是为了用户构建的,但这样难免让我们开发带来一些烦恼,幸亏与一些工具来帮助我们。

Update on reload

这是我最喜欢的:

alt

这样把生命周期变得对开发友好了,每次跳转将会:

1.重新获取 SW

2.尽管字节一致,也会重新安装,也就是说install事件被执行并且更新缓存。

3.跳过 waiting,激活新的 SW。

4.导航到这个页面。

这就是说你每次操作都会更新而不用刷新页面或者关闭 tab。

Skip Waiting

alt
如果你有个 SW 在等待状态,你可以点击 skipWaiting 让它立即变为激活状态。

强制刷新

如果你强制刷新页面,那么会绕过 SW,变成不受控,这个功能已被定为规范,所以在其他支持 SW 的浏览器中也适用。

处理更新

Service Worker 是 可扩展web 的一部分。想法初衷是,作为浏览器开发者,有时候我们可能不如 web 开发者更了解 web,所以,我们其实不应该提供仅仅可以解决具体问题的 API,而是应该给 web 开发者更多的权限从而更好的解决问题。

所以,我们尽可能的开放更多,SW 整个生命周期都是可查看的:

navigator.serviceWorker.register('/sw.js').then(reg => {
  reg.installing; // 安装中的 SW,或者是undefined
  reg.waiting; // 等待中的 SW,或者是undefined
  reg.active; // 激活中的 SW,或者是undefined

  reg.addEventListener('updatefound', () => {
    // 正在安装的新的 SW
    const newWorker = reg.installing;

    newWorker.state;
    // "installing" - 安装事件被触发,但还没完成
    // "installed"  - 安装完成
    // "activating" - 激活事件被触发,但还没完成
    // "activated"  - 激活成功
    // "redundant"  - 废弃,可能是因为安装失败,或者是被一个新版本覆盖

    newWorker.addEventListener('statechange', () => {
      // newWorker 状态发生变化
    });
  });
});

navigator.serviceWorker.addEventListener('controllerchange', () => {
    // 当 SW controlling 变化时被触发,比如新的 SW skippedWaiting 成为一个新的被激活的 SW
});
查看原文

changes 提出了问题 · 2018-11-23

vscode代码格式化的问题

我的vscode用了eslint,在代码保存的时候会进行格式化,所以会把函数的参数格式化成这样

clipboard.png
我不想让它换行,想让它保持我书写时候的样子。

clipboard.png
请问该怎么配置

关注 1 回答 0

changes 提出了问题 · 2018-11-23

vscode代码格式化的问题

我的vscode用了eslint,在代码保存的时候会进行格式化,所以会把函数的参数格式化成这样

clipboard.png
我不想让它换行,想让它保持我书写时候的样子。

clipboard.png
请问该怎么配置

关注 1 回答 0

changes 提出了问题 · 2018-11-23

vscode代码格式化的问题

我的vscode用了eslint,在代码保存的时候会进行格式化,所以会把函数的参数格式化成这样

clipboard.png
我不想让它换行,想让它保持我书写时候的样子。

clipboard.png
请问该怎么配置

关注 1 回答 0

changes 收藏了问题 · 2018-04-26

vscode 插件 beautify 怎么控制对大括号的格式化

我 vscode 格式化用的是 beautify ,平时使用没啥问题,今天写 react ,用import 的时候发生了逼死强迫症的情况
我想要的情况:
我想要的情况
格式化之后的情况
格式化之后的样子

请问要怎么修改配置才能避免这种情况,手动改不说费时的问题,我 formatOnSave 也是开启的.

changes 赞了文章 · 2017-10-19

AJAX总结(二),手写AJAX

前言

博主博客:Stillwater的博客
知乎专栏:前端汪汪
本文为作者原创转载请注明出处:
http://hiztx.top/2017/01/12/a...


在前端面试的时候经常会有如下情景。AJAX会吗?能不能手写个AJAX?第一个问题可以参见我的另一篇博客,AJAX总结(一),AJAX概述。这篇博文我们来回答第二个问题,手写AJAX。


一、手写AJAX的步骤

  手写AJAX并没有一个固定的标准的答案,但是AJAX的关键步骤就那么几步,我会先用文字介绍关键步骤,然后给出两个版本的手写AJAX的代码及注释。帮助大家很好地理解和记忆。

  1. 创建XMLHttpRequest对象

  2. 指定响应函数

  3. 打开连接(指定请求)

  4. 发送请求

  5. 创建响应函数


注:第三步是使用XMLHttpRequest对象的open()方法,字面意思open是打开的意思,即打开连接。但是准确的说应该是指定Http请求。因为浏览器在使用AJAX技术与服务通信时,发送的是Http请求,那么就要指定Http的请求方法,url等信息。

二、参考代码(W3C)

var xmlhttp=null;//声明一个变量,用来实例化XMLHttpRequest对象
if (window.XMLHttpRequest)
  {
  xmlhttp=new XMLHttpRequest();// 新版本的浏览器可以直接创建XMLHttpRequest对象
  }
  
else if (window.ActiveXObject)
  {
  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");// IE5或IE6没有XMLHttpRequest对象,而是用的ActiveXObject对象
  }
  
  
if (xmlhttp!=null)
  {
  xmlhttp.onreadystatechange=state_Change;//指定响应函数为state_Change
  xmlhttp.open("GET","/example/xdom/note.xml",true);//指定请求,这里要访问在/example/xdom路径下的note.xml文件,true代表的使用的是异步请求
  xmlhttp.send(null);//发送请求
  } 
else
  {
  alert("Your browser does not support XMLHTTP.");
  }

//创建具体的响应函数state_Change
function state_Change()
{
if (xmlhttp.readyState==4)
  {
  if (xmlhttp.status==200)
    {
    // 这里应该是函数具体的逻辑
    }
  else
    {
    alert("Problem retrieving XML data");
    }
  }
}
  1. 创建XMLHttpRequest对象 (1-10行代码)

  2. 指定响应函数(第15行代码)

  3. 打开连接(指定请求)(第16行代码)

  4. 发送请求(第18行代码)

  5. 创建响应函数(25-38行代码)

  这个是W3C上讲解AJAX的代码,比较权威,我做了一些注释,方便大家理解。面试的时候写这段代码应该是没有问题的。
W3C原文链接


三、参考代码(MDN)

<!--html部分,创建一个按钮控件-->
<span id="ajaxButton" style="cursor: pointer; text-decoration: underline">
  Make a request
</span>


<script type="text/javascript">
(function() {
  var httpRequest;//声明一个变量,用来实例化XMLHttpRequest对象
  document.getElementById("ajaxButton").onclick = function() { makeRequest('test.html'); }; //这里将AJAX操作封装在makeRequest函数中,函数的参数为要请求的url,即根目录下的test.html文件。
 
  function makeRequest(url) {
    httpRequest = new XMLHttpRequest();//创建XMLHttpRequest对象
    if (!httpRequest) {
      alert('Giving up :( Cannot create an XMLHTTP instance');
      return false;
    }
    
    httpRequest.onreadystatechange = alertContents;//指定响应函数为alertContents
    
    httpRequest.open('GET', url); //指定请求,方法为GET,url为上面的test.html
   
    httpRequest.send();//发送请求
    
  }
  
  
//创建响应函数alertContents
  function alertContents() {
    if (httpRequest.readyState === XMLHttpRequest.DONE) {
      if (httpRequest.status === 200) {
        alert(httpRequest.responseText);
      } else {
        alert('There was a problem with the request.');
      }
    }
  }
})();//这是一个立即执行函数
</script>
  1. 创建XMLHttpRequest对象 (第13行代码)

  2. 指定响应函数(第19行代码)

  3. 打开连接(指定请求)(第21行代码)

  4. 发送请求(第23行代码)

  5. 创建响应函数(29-37行代码)

  这个是MDN上讲解AJAX的代码,我做了一些注释,方便大家理解。
MDN原文链接


四、总结

这篇文章讲解了如何较为规范的手写AJAX,下篇文章我会具体介绍XMLHttpRequest对象的有关知识以及AJAX相关的Http请求的知识。

查看原文

赞 9 收藏 14 评论 0

changes 提出了问题 · 2017-10-12

关于defineProperty的几点疑惑

    function defineReactive(obj, key, val) {
      Object.defineProperty(obj, key, {
        get: function () {
          return val;
        },
        set: function (newVal) {
          val = newVal;
          console.log('值已被修改,现在为 ' + newVal);
        }
      })
    }
    var p1 = {
      name: 'z',
      age: 21,
      sex: 'male'
    };
    
    defineReactive(p1, 'age', p1.age);

执行以后
clipboard.png

几点疑惑
1、这个返回值是哪里来的?是set函数默认添加的吗

clipboard.png

2、JS中函数都是值传递,如果传的是一个基本值类型而不是一个引用类型的话,函数内部形参的改变是不会影响到函数外面的实参的。为什么执行了p1.age = 99 以后,再执行p1.age 能正确取到99呢,这个99存在哪里。。。想了好久都没能理解.... 而且定义get的时候,return val 应该就是 return 21了吧,
我的理解就是一下这两段代码是等价的,因为val参数是一个值类型,所以就是相当于return 21

    function defineReactive(obj, key, val) {
      Object.defineProperty(obj, key, {
        get: function () {
          return val;
        },
        set: function (newVal) {
          val = newVal;
          console.log('值已被修改,现在为 ' + newVal);
        }
      })
    }
    我的理解上面这段代码和下面这段代码等价
    function defineReactive(obj, key, val) {
      Object.defineProperty(obj, key, {
        get: function () {
          return 21;
        },
        set: function (newVal) {
          val = newVal;
          console.log('值已被修改,现在为 ' + newVal);
        }
      })
    }

但实际是为什么set以后,get会改变呢,这个形参val到底经历了什么?

 1   function defineReactive(obj, key, val) {
 2     Object.defineProperty(obj, key, {
 3       get: function () {
 4         return val; // ②
 5       },
 6       set: function (newVal) {
 7         val = newVal; // ①
 8         console.log('值已被修改,现在为 ' + newVal);
 9       }
      })
    }
    var p1 = {
      name: 'z',
      age: 21,
      sex: 'male'
    };
    
    defineReactive(p1, 'age', p1.age);

可能说的不是很明白,刚学这一块,希望得到满意的解答,谢谢~~

关注 3 回答 4

认证与成就

  • 获得 19 次点赞
  • 获得 60 枚徽章 获得 3 枚金徽章, 获得 13 枚银徽章, 获得 44 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2017-06-21
个人主页被 501 人浏览