Object
Object.getOwnPropertySymbols()
获取Symbol值
Object.setPrototypeOf()
设置对象的prototype 用于行为委托
var obj = {
a: 1
}
, handles = {
get(target, key, context) {
console.log("accessing:", key);
return target[key];
},
}
, {proxy: pobj, revoke: prevoke} = Proxy.revocable(obj, handles)
console.log(pobj.a);
// accessing: a
// 1
prevoke();
pobj.a
// Uncaught TypeError: Cannot perform 'get' on a proxy that has been revoked
用于控制生成新实例时,类的内置方法使用哪个构造器
用于生成对象的迭代器
1 | var a = { |
在做类型转换时,将对象转为原生类型值
1 | var arr = [1, 2, 3, 4] |
老说 vue2,数组对象不行,不行在哪?
老说 vue3 上来,这些数组对象的问题都解决了,到底解决了什么?是真的没有问题了吗?
1 | interface Props { |
描述符可以有的属性
configurable | enumerable | value | writable | get | set | |
---|---|---|---|---|---|---|
数据描述符 | 可以 | 可以 | 可以 | 可以 | 不可以 | 不可以 |
存取描述符 | 可以 | 可以 | 不可以 | 不可以 | 可以 | 可以 |
example:
1 | var data = { |
console
遗留问题:
Object.defineProperty
只能根据已存在的 key 来修改get
、set
方法,所以当key
不存在的时候没有办法触发get
和set
所以需要强制刷新,使用$set
数组可以看成key
为 0,1…的对象,也可以使用Object.defineProperty
改写get
和set
,也就是说vue2
可以监控数组的数据变化,
问题 1. 当新增值的时候,数据的新的 key 没有被Object.defineProperty
改写,也就没有办法触发页面刷新,因此需要用$set
问题 2. 当数组unshift
插入值的时候,会修改整个数组的每个对象,所以要触发多次get
,set
, 如下图
1 | var data = ['张三', '李四', '王五'] |
console
解析:数组将内部对象,依次往后移动一位,最后在开始位置插入赵六
数据,所以需要重复的get
,set
特别,是最后一个值王五
,移动到 3 号位置的时候,3 号位置没有被Object.defineProperty
改写,因此打印出来的数据上 3 号位置没有get
、set
方法,也就没有触发set
,console.dir(p)
后也就没有触发get
打印get 3 王五
node_modules/vue/src/core/observer/array.js
1 | const methodsToPatch = [ |
检测数组中的方法有没有被添加数据,如果添加了数据,就将添加的数据用Object.defineProperty
改写。
因此也就解决了,push 后的值没有被监听的问题
因为要对对象的所有属性改写set
、get
方法,所以需要递归遍历
数据的元素,所以性能比较差,所以不要在vue
的data
上挂不需要的值。如window
,jquery
等
1 |
|
example
1 | var data = { |
console
Proxy 不需要遍历 data 的所有 key 修改 key 的 get 和 set,只需要在最上使用 Proxy,就能监听到当前对象 get 和 set。
那么当我们p.name.first="李"
还能触发 set 吗
1 | var data = { |
console
从 console 可以看出,只触发 data 的 name 的 get 方法,没有触发 set,也就是说 Proxy 只能监听代理数据的子项,不能监听代理数据的孙子项。
由于 Proxy 是在 Data 上监听,那么为什么不能在 get 被触发的时候,将子项也代理,从而达到监听孙子项的目的那
1 | var data = { |
console
如图:监听到了 p.name.first 的 set 事件,这样就解决了数据递归遍历的噩运,提高了速度。
1 | var data = ['张三', '李四', '王五'] |
console
当 data 为数组的时候也可以监听到。但是 unshift
哪
1 | var data = ['张三', '李四', '王五'] |
console
由此可见:在 vue3 中使用了 Proxy 也没有解决触发多次 set 事件的问题。
vue3完整示例
1 | function reactive(data) { |
Hook 是React 16.8的新增特性, 它可以在不编写class的情况下使用state以及其他React特性
Hooks增加了函数式组件中state
的使用,在之前函数式组件是无法拥有自己的状态的,只能通过 props 以及 context 来渲染自己的UI, 而在业务逻辑中, 有些场景必须要使用到 state, 那么 我们就只能将函数式组件定义为class 组件.而现在通过 Hook, 我们可以轻松的在函数式组件中保护我们的状态, 不需要更改为class组件
React Hooks 要解决的问题是状态共享, 这里的状态共享是指只共享状态逻辑复用,并不是数据之间的共享.我们知道在React Hooks之间,解决状态逻辑复用问题, 我们通常使用higher-order components 和 render-props.
Hook 最大的优势其实还是对于状态逻辑的复用便捷,还有代码的简洁,以及帮助函数组件增强功能
1 | type Hooks = { |
Reack Hooks 全局维护了一个workInProgressHook变量, 每一次调用 Hooks API 都会首先调取 createWorkInProgressHooks函数
1 | function createWorkInProgressHook(): Hook { |
Hooks 的串联不是一个数组, 是一个链式的数据结构, 从跟节点 workInProgressHook 向下通过next 进行 串联, 这也就是 为什么 Hooks不能嵌套使用,不能在判断条件中使用,不能在循环中使用,否则回破坏链式结构
class组件 | Hooks组件 |
---|---|
constructor | useState |
getDerivedStateFromProps | useState里面的update函数 |
shouldComponentUpdate | useMemo |
render | 函数本身 |
componentDidMount | useLayoutEffect |
componentDidUpdate | useEffect |
componentWillUnmount | useEffect里面返回的函数 |
componentDidCatch | 无 |
getDerivedStateFromError | 无 |
1 | const [state, setState] = useState(initialState); |
setState 函数用于更新 state。它接收一个新的 state 值并将组件的一次重新渲染加入队列。
setState(Object.assign(state,{name:'123'}) // 并不会改变 state中的name
setState({...state,{name:'123'}}) // 每次都要付给他新的值
该 Hook 接收一个包含命令式、且可能有副作用代码的函数。useEffect 的函数会在组件渲染到屏幕之后执行。
useEffect(() => {
const subscription = props.source.subscribe();
return () => {
// 清除订阅
subscription.unsubscribe();
};
},[state]);
当第二个参数不填,默认为所有state,当state更新都,先执行fn返回函数,后执行fn
当第二个参数为[], 取消所有组件监控,当组件更新,先执行fn返回函数,后执行fn
当第二个参数填一个state,则当前state更新则,先执行fn返回函数,后执行fn
保存dom元素
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` 指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
保存可变值 但不会渲染到页面上
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const save = useRef({name:'123'});
const onButtonClick = () => {
// `current` 指向已挂载到 DOM 上的文本输入元素
save.current.value = inputEl.current.value;
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
跨多层组件传值,有父组件向子组件,必须与createContext结合使用
const value = useContext(MyContext);
const themes = {
light: {
foreground: "#000000",
background: "#eeeeee"
},
dark: {
foreground: "#ffffff",
background: "#222222"
}
};
const ThemeContext = React.createContext(themes.light);
function App() {
return (
<ThemeContext.Provider value={themes.dark}>
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() {
const theme = useContext(ThemeContext);
return (
<button style={{ background: theme.background, color: theme.foreground }}>
I am styled by theme context!
</button>
);
}
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
渲染过程中执行,与useEffect不同
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
和useMemo一样,但是返回的是个函数,函数每次渲染都会执行,但值不变;
将组件内的domRef直接暴露到组件本身上
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));
// 你可以直接获取 DOM button 的 ref:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
可以在你使用ref时自定义暴露给父组件实例的值。最好与forwardRef结合使用
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);
第三个参数,监控state的变化
其函数签名与 useEffect 相同,但它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。
某个组件的状态,需要共享
某个状态需要在任何地方都可以拿到
一个组件需要改变全局状态
一个组件需要改变另一个组件的状态
Web 应用是一个状态机,视图与状态是一一对应的。
所有的状态,保存在一个对象里面。
1. Store 保存数据的地方,可以看成一个容器,整个应用中只有一个store
2. State 对象包含的数据,如果想得到某个时点的数据,就要对store生成快照,这个时点的数据集合就叫state
3. Action State的变化会导致View的变化,用户接触不到State,只能接触View,State的变化必须是View导致的,Action就是View发出,用来改变State的
4. Reducer Store收到Action之后,必须给出一个新的State,这样View才会发生变化,这种State的计算过程就叫做Reducer
5. Store.dispatch()发出Action , store.subscribe() 监听变化
“haschange”,“pushstate”,“replacestate”
function test(){
var a = 10 ; //被标记 ,进入环境
var b = 20 ; //被标记 ,进入环境
}
test(); //执行完毕 之后 a、b又被标离开环境,被回收。
function test(){
var a = {} ; //a的引用次数为0
var b = a ; //a的引用次数加1,为1
var c =a; //a的引用次数再加1,为2
var b ={}; //a的引用次数减1,为1
}
全局变量
被遗忘的定时器和回调函数
DOM引用
闭包引用
var theThing = null;
var replaceThing = function () {
var originalThing = theThing;
var unused = function () {
if (originalThing)
console.log("hi");
};
theThing = {
longStr: new Array(1000000).join('*'),
someMethod: function () {
console.log(someMessage);
}
};
unused =null;
};
setInterval(replaceThing, 1000);
为JavaScript 创造多线程环境,
同源限制,分配给worker线程运行的脚本文件,必须与主线程脚本同源
DOM限制,无法使用document,window,parent,可以使用 navigator,location
通信联系, 不能直接通信,只能通过消息完成
vue模块化(懒)加载,vue和react的区别、virtual dom、diff算法等问题。
API数据缓存方案。
axios的实现原理。
1. 传值调用,传名调用
2. 一种传名调用的实现方式
function f(m){
return m * 2;
}
f(x + 5);
// 等同于
var thunk = function () {
return x + 5;
};
function f(thunk){
return thunk() * 2;
}
var fs = require('fs');
var thunkify = require('thunkify');
var readFile = thunkify(fs.readFile);
function run(fn) {
var gen = fn();
function next(err, data) {
var result = gen.next(data);
if (result.done) return;
result.value(next);
}
next();
}
var gen = function* (){
var f1 = yield readFile('./1.json');
console.dir(f1.toString())
var f2 = yield readFile('./2.json');
console.dir(f2.toString())
};
run(gen);
const makeRequest = () =>
getJSON()
.then(data => {
console.log(data)
return "done"
})
makeRequest()
const makeRequest = async() => {
console.log(await getJSON)
return "done"
}
makeRequest()
在函数前有一个关键字async,await关键字只能在async定义的函数中使用,如何一个async的函数都会隐式返回一个promise,并且promise reslove 的值就是return 返回的值
不能在函数开头使用await
Promise有三种状态, reslove,reject, throw, async只有二中reslove throw(reject,throw)
async避免依赖请求的嵌套调用
错误栈,出错后抛出异常,能清楚的打印到哪里出错
代码简介, async让代码看起来更像同步代码,不想Promise 一样需要then
Promise中不能自定义使用 try/catch 进行错误捕获,但是在Async/await中可以
针对每一个元素执行提供的函数
创建一个新的数组,其中每个元素由调用数组中的每个元素执行提供的函数得来
主进程:VSCode 的入口进程,负责一些类似窗口管理、进程间通信、自动更新等全局任务
渲染进程:负责一个 Web 页面的渲染
插件宿主进程:每个插件的代码都会运行在一个独属于自己的 NodeJS 环境的宿主进程中,插件不允许访问 UI
Debug 进程:Debugger 相比普通插件做了特殊化
Search 进程:搜索是一类计算密集型的任务,单开进程保证软件整体体验与性能
loader 是一个转换器,将A文件进行编译成B文件,单纯的文件转换过程,针对文件
plugin 是一个扩展器,丰富了webpack本身,针对loader结束后,webpack打包的整个过程,并不是直接操作文件,而是基于事件机制工作,会监听webpack打包过程中的某些节点,执行广泛的任务,针对工程
减少内存消耗(列表事件绑定,事件绑定到父元素,然后用条件判断)
动态绑定事件
jquery
$.on: 基本用法: $('.parent').on('click', 'a', function () { console.log('click event on tag a'); }),它是 .parent 元素之下的 a 元素的事件代理到 $('.parent') 之上,只要在这个元素上有点击事件,就会自动寻找到 .parent 元素下的 a 元素,然后响应事件;
$.delegate: 基本用法: $('.parent').delegate('a', 'click', function () { console.log('click event on tag a'); }),同上,并且还有相对应的 $.delegate 来删除代理的事件;
$.live: 基本使用方法: $('a', $('.parent')).live('click', function () { console.log('click event on tag a'); }),同上,然而如果没有传入父层元素 $(.parent),那事件会默认委托到 $(document) 上;(已废除)