我们知道,一般用reactive来定义一个响应式对象,ref常用来定义一个响应式的原始值。上篇文章已经聊过了reactive,知晓了如何通过Proxy来对目标对象进行代理从而实现响应式,而非对象的这些原始值的响应式问题就交给ref来解决了。 一、ref 和 shallowRef的函数签名ref和shallowRef各有三种重载,入参各不相同,都返回一个Ref/ShallowRef类型的值。通过createRef函数创建一个响应式的值。和 reactive 相似,reactive也是通过调用createReactiveObject来创建一个响应式的对象。而createRef创建并返回一个 RefImpl 实例。 1234567891011121314151617181920212223242526272829// refexport function ref<T extends object>( value: T): [T] extends [Ref] ? T : Ref<UnwrapRef<T>>;export function ref<T& ...
上次一起阅读了watch和computed的源码,其实应该先看副作用effect,因为各个响应式的API里基本都用到了,等结束了reactive和readonly和ref,就一起看看effect。这次要说的是reactive和readonly,两者在实现上流程大体一致。尤其是对Map和Set的方法的代理拦截,多少有点妙。 一、reactive 和 readonlyVue3使用Proxy来替代Vue2中Object.defineProperty。 12345678910111213const target = { name: "onlyy~",};// 创建一个对target的代理const proxy = new Proxy(target, { // ...各种handler,例如get,set... get(target, property, receiver) { // 其它操作 // ... return Reflect.get(target, property, receiver); ...
想起上次面试,被问了个古老的问题:watch 和 computed 的区别。多少有点感慨,现在已经很少见这种耳熟能详的问题了,网络上八股文不少。今天,我更想分享一下从源码的层面来区别这八竿子打不着的两者。上一篇看了watch的源码,本篇针对computed做分析。 一、类型声明computed的源码在reactivity/src/computed.ts里,先来看看相关的类型定义: ComputedRef:调用computed得到的值的类型,继承自WritableComputedRef; WritableComputedRef:继承自Ref,拓展了一个effect属性; ComputedGetter:传递给ComputedRef的构造器函数,用于创建effect; ComputedSetter:传递给ComputedRef的构造器函数,用于在实例的值被更改时,即在set中调用; WritableComputedOptions:可写的Computed选项,包含get和set,是computed函数接收的参数类型之一。 1234567891011121314151617181920212 ...
想起上次面试,问了个古老的问题:watch 和 computed 的区别。多少有点感慨,现在已经很少见这种耳熟能详的问题了,网络上八股文不少。今天,我更想分享一下从源码的层面来区别这八竿子打不着的两者。本篇针对watch做分析,下一篇分析computed。 一、watch参数类型我们知道,vue3里的watch接收三个参数:侦听的数据源source、回调cb、以及可选的optiions。 1. 选项options我们可以在options里根据需要设置immediate来控制是否立即执行一次回调;设置deep来控制是否进行深度侦听;设置flush来控制回调的触发时机,默认为{ flush: 'pre' },即vue组件更新前;若设置为{ flush: 'post' }则回调将在vue组件更新之后触发;此外还可以设置为{ flush: 'sync' },表示同步触发;以及设置收集依赖时的onTrack和触发更新时的onTrigger两个listener,主要用于debugger。wa ...
一、XMLHttpRequest 对象所有现代浏览器都支持通过XMLHttpRequest构造函数来原生支持XHR对象:const xhr = new XMLHttpRequest()。 1. 使用XHR使用XHR第一步便是调用open()方法,其接收三个参数:请求的类型(如get),url,以及一个布尔值表示请求是否为异步。例如,xhr.open('get', 'www.baidu.com', false) 表示将要向www.baidu.com发送一个同步的get请求。有两点需要注意: 这里得第二个参数url一般是相对于当前页面的,不过也可以使用绝对URL; 调用open()方法只是进行初始化,并不会立即发送请求,而是为发送请求做准备,真正开始发送请求还需要调用send()方法。 send()方法接收一个参数,表示作为请求体发送的数据。xhr.send({username: 'cc', pwd: 'lovecake'})。如果不需要请求体,则必须传入null:xhr.send(null)。调用se ...
ES6新增了class来替代之前的构造函数,并且通过extends关键字可以轻易实现继承。不过ES的概念中,暂时还没有class这一类型,不管从哪方面来看,class都是对之前的继承方案的封装,其本质上是函数(Function的实例) 。了解一下ES6之前的各种继承方案,有助于加深对class继承的理解。 一、创建对象1. 工厂模式当需要创建多个对象实例,且他们的属性高度重复时,无论是通过对象字面量来创建,还是使用new Object来创建,都非常麻烦。采用工厂模式,可以很方便地批量创建多个具有相同属性的对象实例,为此,需要定义一个工厂函数: 123456789101112131415161718192021// 定义一个工厂函数,用于创建对象function createPerson(name, age, gender, ...rest) { // 创建一个空对象person const person = {}; // 将参数上的属性添加到person上(增强对象) person.name = name; person.age = age; ...
自ES6中symbol问世以来,个人在项目中并没有太多机会使用到,小公司业务项目没有给symbol太多的登场机会。因此之前也只稍微知晓了概念,没有详细了解。如今重温js,自然要重新认识一下这独一无二的symbol。如你所知,symbol是原始值(基本数据类型),其每个实例是独一无二且不可变的,一般作为对象属性使用,确保对象属性独一无二,避免属性冲突。 [toc] 1. 基本用法符号只能通过函数来创建实例。最基本的就是Symbol()函数:可以使用Symbol()函数来创建一个符号实例。有以下几点需要注意: Symbol()函数不与new操作符搭配,即new Symbol()是不合法的。这是为了防止创建Symbol包装对象; Symbol()函数可以传入字符串作为键(也可以不传),此时传入的字符串键只起到描述的作用,并不影响symbol实例的值。因此,传入相同的字符串键的symbol实例,其值也不相等而且相互之间也没啥关联。 12345let symbol1 = Symbol("a");let symbol2 = Symbol("a");// ...
一个月前,更新了TS系列。本来要继续其它内容,奈何公司项目开始了,忙到现在,总算能缓一缓。也准备趁机开始源码系列。但是在此之前,我想先温习一下JS系列,查漏补缺,内容主要基于《JS高级程序设计》 1. script标签的 8 个属性 async: 立即下载,但不阻止其它页面动作,如下载资源或等待其它脚本加载;仅对外部脚本文件有效(即src指定路径的文件),保证脚本在页面的load事件之前执行,但不保证脚本执行的顺序。 charset: 使用src属性指定的字符集,这个属性基本不会用到。 crossorigin: 配置相关请求的CORS设置。默认不使用CORS。 defer: 使脚本延迟到页面内容解析之后再执行,html5规范要求脚本按照出现的顺序依次执行,只对外部文件有效,但IE7及更早版本对行内脚本也有效。 intergrity: 允许验证子资源的完整性,一般情况下不会用到。 language: 已废弃。 type: 设置为type="module"才能允许其中的代码被当成ES6模块,从而允许使用import,export等关键字。 src: 表示包含指定的要执行 ...
一、函数式编程的出现 发展史:命令(脚本)式 -> 面向对象 -> 函数式编程 1. 从实际问题开始123456789101112131415161718192021222324252627// 1.数组在 `url` 中展示形式location.search => '?name[]=progressive$%coding&name[]=objective$%coding&name[]=functional$%coding'// 2.参数提取拼接成数组['progressive$%coding', 'objective$%coding', 'functional$%coding']// 3. 手写方法,转换成数组对象// [{name: 'Progressive Coding'}, {name: 'Objective Coding'}, {name: 'Functional ...
模块有自己的作用域,除非进行某种形式的导出,否则,其中的变量,函数,类等都是对外不可见的。相应的,如果要在模块外使用其导出的成员,则需要进行相应的导入。模块的相互导入需要使用 模块加载器 ,模块加载器在执行模块之前,会先定位并执行该模块的依赖项。JS中主要使用Node.js的CommonJs模块加载器和Web应用程序中的AMD模块的RequireJs加载器。TS 延用了ES2015模块化方案,任何包含了顶层的import或export语句的文件,便是一个模块;相反,没有在顶层包含这些语句的则是脚本,其内容在全局作用域中可见。 [toc] 一、导出1. 导出一个声明任何声明 (变量、函数、类、类型等) 都可以使用export关键字来导出。 12345678910111213141516171819export let a = 1;export const b = { a: 2 };export function cc() {}export class CC {}export type A = string | number;ex ...