需求: 显示当前的最新时分秒
一看哇,着很简单啊,刷刷刷开始写代码
1 | import React, { useEffect, useState } from "react"; |
一顿操作猛如虎,运行一看就咋回事跑成这个鬼样子呢?
怎么没正常显示呢
写一个useInterval的hooks,这样时间就正常显示了
1 | import React, { useEffect, useState, useRef } from "react"; |
一看哇,着很简单啊,刷刷刷开始写代码
1 | import React, { useEffect, useState } from "react"; |
一顿操作猛如虎,运行一看就咋回事跑成这个鬼样子呢?
怎么没正常显示呢
写一个useInterval的hooks,这样时间就正常显示了
1 | import React, { useEffect, useState, useRef } from "react"; |
详细文档getx官方中文文档
GetX 是 Flutter 上的一状态管理、依赖注入和路由管理的包
将 Get 添加到你的 pubspec.yaml 文件中。
1 | dependencies: |
在需要用到的文件中导入
1 | import 'package:get/get.dart'; |
Flutter默认创建的 “计数器 “项目有100多行(含注释),为了展示Get的强大功能,我将使用 GetX 重写一个”计数器 Plus版”,实现:
而完成这一切只需 26 行代码(含注释)
1 | void main() => runApp(GetMaterialApp(home: Home())); |
1 | class Controller extends GetxController{ |
1 | class Home extends StatelessWidget { |
要想让它变得可观察,你只需要在它的末尾加上”.obs”。
1 | var name = 'Jonatas Borges'.obs; |
而在UI中,当你想显示该值并在值变化时更新页面,只需这样做。
1 | Obx(() => Text("${controller.name}")); |
这就是全部,就这么简单。
在你的MaterialApp前加上 “Get”,把它变成GetMaterialApp。
1 | GetMaterialApp( // Before: MaterialApp( |
导航到新页面
1 |
|
1 |
|
要关闭snackbars, dialogs, bottomsheets或任何你通常会用Navigator.pop(context)关闭的东西。
1 | Get.back(); |
进入下一个页面,但没有返回上一个页面的选项(用于闪屏页,登录页面等)。
1 | Get.off(NextScreen()); |
进入下一个页面并取消之前的所有路由(在购物车、投票和测试中很有用)。
1 | Get.offAll(NextScreen()); |
Get依赖管理器就是导入状态管理
注入依赖:
1 | Get.put<PutController>(PutController()); |
获取依赖:
1 | Controller putController = Get.find<PutController>(); |
提示: Get依赖管理与包的其他部分是解耦的,所以可以导入多个
Get.find()
,它可以帮你找到控制器,这个东西也是有限制的,这东西就找不到直接实例化的控制器,只能找到通过依赖注入
的控制器
1 | class ListPage extends StatelessWidget { |
Nest 操作 Mongodb 官方文档:https://docs.nestjs.com/techniques/mongodb
1 | npm install --save @nestjs/mongoose mongoose |
在 app.module.ts 中配置数据库连接
1 | import { Module } from '@nestjs/common'; |
1 | import * as mongoose from 'mongoose'; |
1 | import { Module } from '@nestjs/common'; |
1 | import { Injectable } from '@nestjs/common'; |
有如下一个数据,但它是用数组存放的具有联级关系的数据,需要将其转化为适合UI库的数据结构树状结构
1 | const data = [ |
1 | [ |
使用String.prototype.split()
将字符串切分为数组,方便分级
1 | [ |
简单点的就是死办法,看数据貌似只有4级,所以套四重循环就可以解决
但是这样真的很蠢,很笨,一旦数据发生变化,这就白写了
按照思路来先把数组中的字符串进行切分
1 | const treeArr = [] |
观察切分好的数据结构,有的是只有一级,没有子级的,有的呢,可能有四级也可能有三级。这样就对分级存放数据结构带来了很大的困扰。
这样就需要对每一条数据进行判断,这个判断的过程就抽出来写成一个函数就行了。
1 | const treeArr = [] |
这个函数接收3个传参
judgeData
函数负责对传进来的每一条原始的字符串切分数组,进行判断,如果符合要求就push
,不符合要求就递归判断,关键是递归的思想
1 | const judgeData = (treeArr, eleArr, step) => { |
treeArr
传入的并不是预先在最开始设置的那个treeArr,而是每次存放预期数据结构的数组,因为js的对象是引用类型的所以,从对象上拆children下来传进去,并不影响存值,而step则是为了记录分级的层级,避免不知道到哪一步了,乱分。
这里贴上完整代码
1 | const data = [ |
先来看一个基本的构造函数
1 | function Person(name = '人') { |
现在给构造函数中的this
添加一个方法:say()
可以看到,两个实例p1
和p2
的say
方法是不相等的,而每创建一个实例,都会额外创建一个新的函数,这样显然是不好的,需要有一种方法,使得创建的实例的方法是共享的
1 | function Person(name = '人') { |
于是,原型对象就出现了
js中一切都是对象,所以函数也是对象,所有的函数都会拥有一个prototype
属性,这个属性指向的对象是这个函数的原型对象,构造函数的原型对象上的属性和方法都会被它的实例所继承
1 | function Person(name = '人') { |
而原型对象上有一个属性constructor
,保存的是它的那个构造函数的引用
所以下面可以看到Person.prototype
把原型对象给指了出来,但又被Person.prototype.constructor
给指了回去
1 | function Person(name = '人') { |
那构造函数的实例有什么办法可以获取到生成它的构造函数吗?
是可以的,每个实例上都会有constructor
属性,直接指向了它的构造函数的原型对象,同时呢每个实例上都会有一个__proto__
属性,直接指向了它的构造函数的原型对象,再通过constructor
指回来它的构造函数就行
1 | function Person(name = '人') { |
所以呢这就形成了一个环,如下图
但最终原型链应该是一个“链”
也就是说可以顺着原型对象一直指下去,对的
原型对象本身也具有__proto__
属性,直接指向了它的构造函数,当一个实例调用它的方法在它的构造函数中找不到时,就回去原型对象上去找,当原型对象找到不的时候就会去原型对象的原型对象上去找,直到原型链的尽头。
一个开放标准,对于开发人员可互操作的 JavaScript 承诺
一个 promise 代表一个异步操作的最终结果。主要的操作方式是通过调用 promise 的 then
方法,它接受的回调函数接受 promise 成功的结果或失败的原因
这个规范详细的描述了 then
方法的行为,提供一个互操作基础,所有符合 Promises/A+ 的都可以依赖这个标准实现。因此,该规范已经十分稳定。尽管 Promises/A+ 组织可能会偶尔修改以实现向后兼容,我们也会整合这些大的或不能向后兼容的改变,一起研究,讨论,测试。
曾经, Promises/A+ 解释了早期 PromisesA 提议的条款,扩展了事实上的行为和忽略了不标准和有问题的部分。
最终,Promises/A+ 规范并没处理如何创建 fulfill,或 reject promise,而选择了可互操作的 then
方法替代。在今后的工作中可能会考虑。
1.1 promise
是一个有符合此标准的 then
方法的 object
或 function
1.2 thenable
是 then
方法定义的 object
或 function
1.3 value
是一个 JavaScript 合法值(包括 undefined
,thenable,promise)
1.4 exception
是一个 throw
语句抛出错误的值
1.5 reason
是一个表明 promise 失败的原因的值
一个 promise 有且只有一个状态(pending,fulfilled,rejected 其中之一)
2.1.1 pending 状态时:
2.1.2 fulfilled 状态时:
2.1.2.1 不能再状态为任何其他状态
2.1.2.2 必须有一个 value,且不可改变
2.1.3 rejected 状态时:
2.1.3.1 不能再状态为任何其他状态
2.1.3.2 必须有一个 reason,且不可改变
注:这里 不可改变
意思是不可变恒等(同理 === ),但不意味永远不可变
一个 promise 必须提供一个 then
方法,用来获取当前或最终的 value 或 reason
一个 promise 的 then
方法接受两个参数:
promise.then(onFulfilled, onRejected)
2.2.1 onFulfilled 和 onRejected 都是可选参数:
2.2.1.1 如果 onFulfilled 不是函数,它会被忽略
2.2.1.2 如果 onRejected 不是函数,它会被忽略
2.2.2 如果 onFulfilled 是一个函数:
2.2.2.1 它一定是在 promise 是 fulfilled 状态后调用,并且接受一个参数 value
2.2.2.2 它一定是在 promise 是 fulfilled 状态后调用
2.2.2.3 它最多被调用一次
2.2.3 如果 onRejected 是一个函数:
2.2.3.1 它一定在 promise 是 rejected 状态后调用,并且接受一个参数 reason
2.2.3.2 它一定在 promise 是 rejected 状态后调用
2.2.3.3 它最多被调用一次
2.2.4 onFulfilled 或 onRejected 只在执行环境堆栈只包含平台代码之后调用 [3.1]
2.2.5 onFulfilled 和 onRejected 会作为函数形式调用 (也就是说,默认 this
指向 global,严格模式 undefined
) [3.2]
2.2.6 在同一个 promise 实例中,then
可以链式调用多次
2.2.6.1 如果或当 promise 转态是 fulfilled 时,所有的 onFulfilled 回调回以他们注册时的顺序依次执行
2.2.6.2 如果或当 promise 转态是 rejected 时,所有的 onRejected 回调回以他们注册时的顺序依次执行
2.2.7 then 方法一定返回一个 promise
promise2 = promise1.then(onFulfilled, onRejected);
[[Resolve]](promise2, x)
处理解析
2.2.7.2 如果 onFulfilled 或 onRejected 里抛出了一个异常,那么 promise2 必须捕获这个错误(接受一个 reason 参数)
2.2.7.3 如果 onFulfilled 不是一个函数,并且 promise1 状态是 fulfilled,那么 promise2 一定会接受到与 promse1 一样的值 value
2.2.7.4 如果 onRejected 不是一个函数,并且 promise1 状态是 rejected,promise2 一定会接受到与 promise1 一样的值 reason
promise 处理程序是一个表现形式为 [[Resolve]](promise, x) 的抽象处理操作。如果 x 是 thenable 类型,它会尝试生成一个 promise 处理 x,否则它将直接 resolve x
只要 then 方法符合 Promises/A+ 规则,那么对 thenables 处理就允许实现可互操作(链式调用,层层传递下去)。它也允许对那些不符合 Promises/A+ 的 then 方法进行 “吸收”
[[Resolve]](promise, x)
的执行表现形式如下步骤:
2.3.1 如果返回的 promise1 和 x 是指向同一个引用(循环引用),则抛出错误
2.3.2 如果 x 是一个 promise 实例,则采用它的状态:
2.3.2.1 如果 x 是 pending 状态,那么保留它(递归执行这个 promise 处理程序),直到 pending 状态转为 fulfilled 或 rejected 状态
2.3.2.2 如果或当 x 状态是 fulfilled,resolve 它,并且传入和 promise1 一样的值 value
2.3.2.3 如果或当 x 状态是 rejected,reject 它,并且传入和 promise1 一样的值 reason
2.3.3 此外,如果 x 是个对象或函数类型
2.3.3.1 把 x.then
赋值给 then
变量
2.3.3.2 如果捕获(try
,catch
)到 x.then
抛出的错误的话,需要 reject 这个promise
2.3.3.3 如果 then
是函数类型,那个用 x 调用它(将 then
的 this
指向 x),第一个参数传 resolvePromise ,第二个参数传 rejectPromise:
2.3.3.3.1 如果或当 resolvePromise 被调用并接受一个参数 y 时,执行
[[Resolve]](promise, y)
2.3.3.3.2 如果或当 rejectPromise 被调用并接受一个参数 r 时,执行 reject(r)
2.3.3.3.3 如果 resolvePromise 和 rejectPromise 已经被调用或以相同的参数多次调用的话吗,优先第一次的调用,并且之后的调用全部被忽略(避免多次调用)
2.3.3.4 如果 then
执行过程中抛出了异常,
2.3.3.3.4.1 如果 resolvePromise 或 rejectPromise 已经被调用,那么忽略异常
2.3.3.3.4.2 否则,则 reject 这个异常
2.3.3.4 如果 then
不是函数类型,直接 resolve x(resolve(x))
2.3.4 如果 x 即不是函数类型也不是对象类型,直接 resolve x(resolve(x))
如果被 resolve 的 promise 参与了 thenable 的循环链中,那么可能会导致无限递归。我们鼓励实现检测这种无限递归的方法并且返回一个错误信息,但并不是必须的 [3.6]
3.1 这里的 “平台代码”是指引擎,环境,和 promise 实现代码。实际上,这个要求确保 onFulfilled 和 onRejected 都在下一轮的事件循环中(一个新的栈)被异步调用。可以用宏任务,例如:setTimeout
,setImmediate
或者微任务,例如:MutationObsever
或 process.nextTick
实现。 由于 promise 的实现被当做平台代码,所以它本身可能包含一个任务队列或 “trampoline” 的处理程序
3.2 这个 this
在严格模式下是 undefined
,在宽松模式,指向 global 对象
3.3 具体的实现可以允许 promise2 和 promise1 绝对相等,要满足所有要求。每一个处理 promise2 和 promise1 绝对相等的实现都要写上文档标注
3.4 通常,只有它来自当前实现才可以判断 x 是一个真正的 promise。 此条款允许采取已知符合 promise 标准实现的状态
3.5 把 x.then
存起来,然后测试、调用这个引用,避免多次访问 x.then
属性。这么做的原因是防止每次获取 x.then
时,返回不同的情况(ES5
的 getter
特性可能会产生副作用)
3.6 实现不应该武断地限制 thenable 链的深度,假设超出限制的无限递归。只有真正的循环引用才会导致一个 TypeError
错误,如果遇到一个不同的无限递归 thenable 链,一直递归永远是正确的行为
注:本文不以盈利为目的,仅做学习交流使用,若有侵权,请联系我删除,万分感谢!
可以用*
来转化为数字 (实际上是调用.valueOf方法)
然后使用Number.isNaN来判断是否为NaN,或者使用 a !== a 来判断是否为NaN,因为 NaN !== NaN
1 | '32' * 1 // 32 |
常用:也可以使用+
来转化字符串为数字
1 | + '123' // 123 |
可以使用 字符串 +Object 的方式来转化对象为字符串 (实际上是调用 .toString() 方法)
1 | 'the Math object:' + Math // "the Math object:[object Math]" |
当然也可以覆盖对象的toString和valueOf方法来自定义对象的类型转换:
1 | 2 * { valueOf: () => '3' } // 6 |
《Effective JavaScript》P11:当+用在连接字符串时,当一个对象既有toString方法又有valueOf方法时候,JS 通过盲目使用valueOf方法来解决这种含糊。
对象通过valueOf方法强制转换为数字,通过toString方法强制转换为字符串
1 | '' + { toString: ()=> 'S', valueOf: () => 'J' } // J |
我们知道 JS 中有一些假值:false,null,0,””,undefined,NaN,怎样把数组中的假值快速过滤呢,可以使用 Boolean 构造函数来进行一次转换
1 | const compact = arr => arr.filter(Boolean) |
可以使用双位操作符来替代 Math.floor( )。双否定位操作符的优势在于它执行相同的操作运行速度更快。
1 | Math.floor(4.9) === 4 //true |
不过要注意,对整数来说 ~~ 运算结果与 Math.floor( ) 运算结果相同,而对于负数来说不相同:
1 | ~~4.5 // 4 |
我们知道逻辑与&&与逻辑或||是短路运算符,短路运算符就是从左到右的运算中前者满足要求,就不再执行后者了;
可以理解为:
&&
为取假运算,从左到右依次判断,如果遇到一个假值,就返回假值,以后不再执行,否则返回最后一个真值。
||
为取真运算,从左到右依次判断,如果遇到一个真值,就返回真值,以后不再执行,否则返回最后一个假值。
因此可以用来做很多有意思的事,比如给变量赋初值:
1 | let variable1 |
如果 variable1 是真值就直接返回了,后面短路就不会被返回了,如果为假值,则会返回后面的foo。
也可以用来进行简单的判断,取代冗长的if语句:
1 | let variable = param && param.prop |
如果param如果为真值则返回param.prop属性,否则返回param这个假值,这样在某些地方防止param为undefined的时候还取其属性造成报错。
对一个数字| 0可以取整,负数也同样适用,num | 0
1 | 1.3 | 0 // 1 |
对一个数字& 1可以判断奇偶数,负数也同样适用,num & 1
1 | const num = 3; |
1 | func = (l, m = 3, n = 4 ) => (l * m * n); |
注意,传入参数为undefined或者不传入的时候会使用默认参数,但是传入null还是会覆盖默认参数。
默认情况下,如果不向函数参数传值,那么 JS 会将函数参数设置为undefined。其它一些语言则会发出警告或错误。要执行参数分配,可以使用if语句抛出未定义的错误,或者可以利用强制参数。
1 | mandatory = () => { |
返回值是我们通常用来返回函数最终结果的关键字。只有一个语句的箭头函数,可以隐式返回结果(函数必须省略大括号{ },以便省略返回关键字)。
要返回多行语句(例如对象文本),需要使用( )而不是{ }来包裹函数体。这样可以确保代码以单个语句的形式进行求值。
1 | function calcCircumference(diameter) { |
在某个场景下我们的函数中有判断语句,这个判断依据在整个项目运行期间一般不会变化,所以判断分支在整个项目运行期间只会运行某个特定分支,那么就可以考虑惰性载入函数
1 | function foo(){ |
那么第一次运行之后就会覆写这个方法,下一次再运行的时候就不会执行判断了。当然现在只有一个判断,如果判断很多,分支比较复杂,那么节约的资源还是可观的。
跟上面的惰性载入函数同理,可以在函数体里覆写当前函数,那么可以创建一个一次性的函数,重新赋值之前的代码相当于只运行了一次,适用于运行一些只需要执行一次的初始化代码
1 | var sca = function() { |
虽然将 foo.bar 写成 foo [‘bar’] 是一种常见的做法,但是这种做法构成了编写可重用代码的基础。许多框架使用了这种方法,比如 element 的表单验证。
请考虑下面这个验证函数的简化示例:
1 | function validate(values) { |
上面的函数完美的完成验证工作。但是当有很多表单,则需要应用验证,此时会有不同的字段和规则。如果可以构建一个在运行时配置的通用验证函数,会是一个好选择。
1 | // object validation rules |
现在有了这个验证函数,我们就可以在所有窗体中重用,而无需为每个窗体编写自定义验证函数。
ES6 中新增了不同进制的书写格式,在后台传参的时候要注意这一点。
1 | 29 // 10 进制 |
将数字四舍五入到指定的小数位数。使用 Math.round() 和模板字面量将数字四舍五入为指定的小数位数。 省略第二个参数 decimals ,数字将被四舍五入到一个整数。
1 | const round = (n, decimals = 0) => Number(`${Math.round(`${n}e${decimals}`)}e-${decimals}`) |
感谢网友 @JserWang @vczhan 提供 这个小技巧
有时候比如显示时间的时候有时候会需要把一位数字显示成两位,这时候就需要补 0 操作,可以使用slice和 string 的padStart方法
1 | const addZero1 = (num, len = 2) => (`0${num}`).slice(-len) |
假设现在有一个数列,你希望更新它的每一项(map 的功能)然后筛选出一部分(filter 的功能)。如果是先使用 map 然后 filter 的话,你需要遍历这个数组两次。
在下面的代码中,我们将数列中的值翻倍,然后挑选出那些大于 50 的数。
1 | const numbers = [10, 20, 30, 40]; |
很多时候,你希望统计数组中重复出现项的个数然后用一个对象表示。那么你可以使用 reduce 方法处理这个数组。
下面的代码将统计每一种车的数目然后把总数用一个对象表示。
1 | var cars = ['BMW','Benz', 'Benz', 'Tesla', 'BMW', 'Toyota']; |
有时候你会将函数返回的多个值放在一个数组里。我们可以使用数组解构来获取其中每一个值。
1 | let param1 = 1; |
当然我们关于交换数值有不少其他办法:
1 | var temp = a; a = b; b = temp |
使用递归,为每个深度级别 depth 递减 1 。 使用 Array.reduce() 和 Array.concat() 来合并元素或数组。 基本情况下,depth 等于 1 停止递归。 省略第二个参数,depth 只能平铺到 1 (单层平铺) 的深度。
1 | const flatten = (arr, depth = 1) => |
数组也可以对象解构,可以方便的获取数组的第 n 个值
1 | const csvFileLine = '1997,John Doe,US,john@doe.com,New York'; |
有时候你不希望保留某些对象属性,也许是因为它们包含敏感信息或仅仅是太大了(just too big)。你可能会枚举整个对象然后删除它们,但实际上只需要简单的将这些无用属性赋值给变量,然后把想要保留的有用部分作为剩余参数就可以了。
下面的代码里,我们希望删除 _internal 和 tooBig 参数。我们可以把它们赋值给 internal 和 tooBig 变量,然后在 cleanObject 中存储剩下的属性以备后用。
1 | let {_internal, tooBig, ...cleanObject} = {el1: '1', _internal: "secret", tooBig: {}, el2: '2', el3: '3'}; |
在下面的代码中,engine 是对象 car 中嵌套的一个对象。如果我们对 engine 的 vin 属性感兴趣,使用解构赋值可以很轻松地得到它。
1 | var car = { |
界面展示的时间千变万化, 所以一个处理时间的函数,它的重要性就不言而喻了。
1 | export function formatDate (oldDate, fmt) { |
formatDate 接受俩个参数, oldDate 类型可以是 Date,String,Number。因为现在用时间戳传递时间真的是蛮多,也蛮方便的,而JS 是一门弱类型语言,所以我将 String 和 Number 类型的数据统一当做时间戳来处理。
fmt 是格式化的类型:yyyy-MM-dd hh:mm,其中的 yyyy | MM | dd | hh | mm 是分别匹配 年 | 月 | 日 | 时 | 分 的关键字。其中的连字符是可以随意替换的,只展示年月将其他关键字去掉即可。举几个例子:
通常都会获取三天前的时间,12 个以内的数据,24 小时以内的数据,因此我弄了一个以天为单位获取时间戳的函数
1 | export function setDate(num) { return Date.now() + num * 24 * 60 * 60 * 1000} |
时间为正可以获得未来的时间,时间为负可以获得过去时间。举个例子:
这个需求在三大框架的时代应用貌似不多了,不过面试中问的还是蛮多的,了解一下是好的
简单实现
1 | var urlParams = new URLSearchParams('?post=1234&action=edit'); |
看了一下浏览器支持情况还是蛮好的,除了万恶的 ie复杂实现function
1 | getUrlParams(param){ |
举个例子: http://xuyuechao.top?a=3&b=5&c=8888
1 | BrowserInfo = { |
目前主要支持 安卓 & 苹果 & ipad & 微信 & 支付宝 & 是否是手机端。
二维数组
1 | let arr = [ [1], [2], [3] ] |
多维数组降维
1 | let arr = [1, 2, [3], [[4]]] |
flat 有兼容性问题,手机端问题不大。浏览器端不兼容 edge。填 Infinity 可展开任意深度的数组
使用变量 a 拷贝对象 b,改变 a 中的值 b 中的值也会跟着改变,这叫做浅拷贝。要想让 a 独立于 b 就需要深拷贝
简易处理
1 | function deepClone() { |
既然是简易处理就有他的不足,上面主要是用了 JSON 的序列化和反序列化。而 JSON 是不支持函数和 undefined 的因此碰到这些情况会缺失,但是已经能够满足大部分情况了复杂处理
复杂处理就需要采用递归的方式了
1 | function deepClone(obj) { |
防抖和节流属于高阶技巧,业务中比较多见的场合也就是搜索内容改变提示信息。即使不加也也不一定能看出区别,但是加了新手维护代码可能会崇拜你哦。
防抖
1 | function debounce(func, wait) { |
节流
1 | function throttle(func, wait) { |
1 | function smallest(array){ |
用 es6 的实现方式
1 | let list = [1, 2, 3, 4, 5] |
1 | function epsEqu(x,y) { |
Number.EPSILON ,Number.EPSILON === Math.pow(2, -52)
因此上述方法也可以这么写
1 | function epsEqu(x,y) { |
用户输入的是十进制数字,计算机保存的是二进制数。所以计算结果会有偏差,所以我们不应该直接比较非整数,而是取其上限,把误差计算进去。这样一个上限称为 machine epsilon,双精度的标准 epsilon 值是 2^-53 (Math.pow(2, -53))
学会这些 JS 小技巧,提升编码幸福度
收下这波 JS 技巧,从此少加班
注:本文不以盈利为目的,仅做学习交流使用,若有侵权,请联系我删除,万分感谢!
1 | foo = 'abc' |
1 | var boat = { |
首先要明白,在任何函数中,this的指向都不是静态的(static)。它总是在你调用一个函数,但尚未执行函数内部代码前被指定。实际上,this是 被调用的函数的父作用域提供的。
this,指向调用它所属的那个函数的那个对象
1 | function bar() { |
由于js中Object和Function是引用类型,所以在引用发生变化的时候,this的指向也会发生改变
1 | var foo = { |
嵌套在对象里的this的指向
1 | var anum = 0 |
值得注意的是下面这几种用法,都会改变this的指向。
1 | var obj ={ |
上面代码中,obj.foo就是一个值。这个值真正调用的时候,运行环境已经不是obj了,而是全局环境,所以this不再指向obj
上面三种情况等同于下面的代码。
1 | // 情况一 |
如果this所在的方法不在对象的第一层,这时this只是指向当前一层的对象,而不会继承更上面的层。
1 | var obj = { |
此时的this指向的son,son没有name属性,就当然undefined了
构造函数中的this,指的是实例对象。
1 | var Obj = function (x) { |
类中的this,指的是实例对象。
1 | class Obj { |
1 | <div id="test">今天是个好日子</div> |
注:本文不以盈利为目的,仅做学习交流使用,若有侵权,请联系我删除,万分感谢!
vue-function-api
使开发者们可以在 Vue2.x
中使用 Vue3
引入的基于函数的逻辑复用机制。
npm
1 | npm install vue-function-api --save |
yarn
1 | yarn add vue-function-api |
CDN
1 | <script src="https://unpkg.com/vue-function-api/dist/vue-function-api.umd.js"></script> |
通过全局变量 window.vueFunctionApi
来使用。
CodePen
在线示例,fork 后进行测试或 bug 反馈。
您必须显式地通过 Vue.use()
来安装 vue-function-api
:
1 | import Vue from 'vue' |
安装插件后,您就可以使用新的函数式 API来书写组件了。
1 | <template> |
▸ setup(props: Props
, context: Context
): Object|undefined
组件现在接受一个新的 setup
选项,在这里我们利用函数 api 进行组件逻辑设置。
setup()
中不可以使用 this
访问当前组件实例, 我们可以通过 setup
的第二个参数 context
来访问 vue2.x API 中实例上的属性。
1 | const MyComponent = { |
▸ value(value: any
): Wrapper
value
函数为组件声明一个响应式属性,我们只需要在 setup
函数中返回 value
的返回值即可。
Example:
1 | import { value } from 'vue-function-api' |
▸ state(value: any
)
与 Vue.observable
等价。
Example:
1 | import { state } from 'vue-function-api' |
▸ computed(getter: Function
, setter?: Function
): Wrapper
与 vue 2.x computed property 行为一致。
Example:
1 | import { value, computed } from 'vue-function-api' |
▸ watch(source: Wrapper | () => any
, callback: (newVal, oldVal)
, options?: WatchOption
): Function
▸ watch(source: Array<Wrapper | () => any>
, callback: ([newVal1, newVal2, ... newValN], [oldVal1, oldVal2, ... oldValN])
, options?: WatchOption
): Function
watch
允许我们在相应的状态发生改变时执行一个回调函数。
返回值 Function
一个可调用的函数来停止 watch
。
effect-cleanup 当前并不支持。
Name | Type | Default | Description |
---|---|---|---|
lazy | boolean |
false |
与 2.x watch 中的 immediate 选项含义相反. |
deep | boolean |
false |
与 2.x 相同 |
flush | "pre" | "post" | "sync" |
"post" |
"post" : render 后触发; "pre" : render 前触发, "sync" : 同步触发 |
Example:
1 | watch( |
Example(Multiple Sources):
1 | watch( |
▸ onCreated(cb: Function
)
▸ onBeforeMount(cb: Function
)
▸ onMounted(cb: Function
)
▸ onXXX(cb: Function
)
支持 2.x 中除 beforeCreated
以外的所有生命周期函数,额外提供 onUnmounted
。
Example:
1 | import { onMounted, onUpdated, onUnmounted } from 'vue-function-api' |
▸ provide(key: string
| symbol
, value: any
)
▸ inject(key: string
| symbol
)
与 2.x 中的 provide
和 inject
选项行为一致。
Example:
1 | import { provide, inject } from 'vue-function-api' |
Context
对象中的属性是 2.x 中的 vue 实例属性的一个子集。完整的属性列表:
以下内容引自 尤雨溪知乎专栏
一个包装对象只有一个属性:value
,该属性指向内部被包装的值。
我们知道在 JavaScript 中,原始值类型如 string
和 number
是只有值,没有引用的。如果在一个函数中返回一个字符串变量,接收到这个字符串的代码只会获得一个值,是无法追踪原始变量后续的变化的。
因此,包装对象的意义就在于提供一个让我们能够在函数之间以引用的方式传递任意类型值的容器。这有点像 React Hooks 中的 useRef
—— 但不同的是 Vue 的包装对象同时还是响应式的数据源。有了这样的容器,我们就可以在封装了逻辑的组合函数中将状态以引用的方式传回给组件。组件负责展示(追踪依赖),组合函数负责管理状态(触发更新):
1 | setup() { |
包装对象也可以包装非原始值类型的数据,被包装的对象中嵌套的属性都会被响应式地追踪。用包装对象去包装对象或是数组并不是没有意义的:它让我们可以对整个对象的值进行替换 —— 比如用一个 filter 过的数组去替代原数组:
1 | const numbers = value([1, 2, 3]) |
如果你依然想创建一个没有包装的响应式对象,可以使用 state
API(和 2.x 的 Vue.observable()
等同):
1 | import { state } from 'vue' |
当包装对象被暴露给模版渲染上下文,或是被嵌套在另一个响应式对象中的时候,它会被自动展开 (unwrap) 为内部的值。
比如一个包装对象的绑定可以直接被模版中的内联函数修改:
1 | const MyComponent = { |
当一个包装对象被作为另一个响应式对象的属性引用的时候也会被自动展开:
1 | const count = value(0) |
以上这些关于包装对象的细节可能会让你觉得有些复杂,但实际使用中你只需要记住一个基本的规则:只有当你直接以变量的形式引用一个包装对象的时候才会需要用 .value
去取它内部的值 —— 在模版中你甚至不需要知道它们的存在。
Vue2.x
的公共 API 限制,vue-function-api
无法避免的会产生一些额外的内存负载。如果您的应用并不工作在极端内存环境下,无需关心此项。注:本文不以盈利为目的,仅做学习交流使用,若有侵权,请联系我删除,万分感谢!
array.prototype.reduce()
回调函数
执行数组中每个值的函数,包含四个参数:
累积变量,默认为数组的第一个成员
或’自定义累积变量’(见于下方)
当前变量,默认为数组的第二个成员
数组中正在处理的元素
当前位置 可选
数组中正在处理的当前元素的索引。 如果提供了initialValue,则起始索引号为0,否则为1
原数组 可选
调用reduce()的原数组
自定义累积变量 可选
作为第一次调用 回调函数 时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错。
注意:自定义累积变量 不是替换掉数组的第一个元素!
1 | [1, 3, 5, 7, 9].reduce((x, y) => x + y) // 25 |
1 | [2, 3, 5].reduce((x, y) => x * y) // 30 |
1 | [1, 3, 5, 7, 9].reduce((x, y) => x * 10 + y) // 13579 |
'13579'
先变成Array [1, 3, 5, 7, 9],再利用reduce() 写出一个把字符串转换为Number的函数不要使用JavaScript内置的parseInt()函数,利用map和reduce操作实现一个string2int()函数
1 | ; |
array.prototype.reduceRight()
reduce
方法和reduceRight
方法依次处理数组的每个成员,最终累计为一个值。它们的差别是,reduce
是从左到右处理(从第一个成员到最后一个成员),reduceRight
则是从右到左(从最后一个成员到第一个成员),其他完全一样。
1 | function subtract(prev, cur) { |
由于这两个方法会遍历数组,所以实际上还可以用来做一些遍历相关的操作。比如,找出字符长度最长的数组成员。
1 | function findLongest(entries) { |
上面代码中,reduce的参数函数会将字符长度较长的那个数组成员,作为累积值。这导致遍历所有成员之后,累积值就是字符长度最长的那个成员。
注:本文不以盈利为目的,仅做学习交流使用,若有侵权,请联系我删除,万分感谢!