-
Notifications
You must be signed in to change notification settings - Fork 0
/
search.xml
157 lines (157 loc) · 68.3 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[Redux 相关]]></title>
<url>%2F2020%2F11%2F08%2FRedux-%E7%9B%B8%E5%85%B3%2F</url>
<content type="text"></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>状态管理</tag>
</tags>
</entry>
<entry>
<title><![CDATA[react-router]]></title>
<url>%2F2020%2F11%2F08%2Freact-router%2F</url>
<content type="text"><![CDATA[hash 和 history对于单页面应用和多页面应用的url的形式区分是a.com/#/pageone和a.com/pageone.html。在还没有单页面应用的时期,浏览器多充当的是一个展现层,路由处理、服务调用、页面跳转流程都由服务端来处理。看图理解……吧如果是MPA的话,前端是不需要进行路由处理的,而转入SPA的话,大多数时候,对于后端的请求不再是整个html文件,而是“数据”,除了数据之外的有关css,html,js文件之间处理全由前端处理,前端便自然要处理页面路由。……待写 react-router 官网学习记录在react应用中,获取当前页面路由及代码实现路由切换参考文档 一本关于单页面应用的gitbook]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>页面路由</tag>
</tags>
</entry>
<entry>
<title><![CDATA[react hooks in practice]]></title>
<url>%2F2020%2F09%2F28%2Freact-hooks-in-practice%2F</url>
<content type="text"><![CDATA[useState和 react 的 this.state 是一个东西useEffect在 component.DidMount 和 componnet.DidUpdate 这两个生命周期内执行。如果在useEffect中有return的话,return部分在componnetWillUnmount阶段调用。 关于 useEffect 第二个参数的三种情况:codesandbox 不写。视情况而定,会发生语法报错,说:缺失依赖值,可能会导致对依赖的无限查找。不写就是默认对你所有用到的值变化的时候执行。 空数组[]。当useEffect 中用到了依赖值,却设置了空依赖列表,会发生报错,强制忽略报错,可以用来阻止代码执行。 正常情况下的所用值形成的依赖列表。 useEffect 的应用: 有些在浏览器中才能使用的,而在build阶段不可以使用的,比如 localstorage, react router中的location.state.xxx,这部分的代码可以写为 useEffect(...some code...,[]) 结合 useRef 写一个自定义hook储存 previous state。(下文中的demo还显示了 useRef 和 组件外部变量的应用区别。)codesandbox 设置定时器,对input输入内容的防抖。oi-wiki-search.jscodesandbox useContext当一个较深层次的子组件要使用父组件的一些值的时候,使用useContext可以避免props层层传递的累赘,而直接在context.provider包裹下的组件中,使用context.consumer或者useContext获取顶层父组件的值(provider value),而不单单是上一层父组件的值。 可以应用在主题和语言设置上。intl context provider部分intl context consumer部分intl useContext 部分 或许可以考虑写一个单独demo出来?? useCallback 、 useMemo and React.Memo这三者都有很大的相似性。react 在对props进行相等性检查时,对于相同值但是代码来源不同的数组和对象的对比结果会为false,这就导致很多不必要的重计算和重渲染,这三个API都可以用来处理这个问题。 1234567//相等性检查const n1 = 1const n2 = 1n1 === n2 // trueconst a1 = [1]const a2 = [1]a1 === a2 // false 在下面这个demo可以看到,options的值没有变化,可是由于js的工作机制,在上层组件(在demo中上层组件是App)发生更新时,每次传入 Apple 组件的参数,都被认为是新的,useEffect的回调函数进行了re-run,(re-render了吗?可由devtools监测到HTML元素),并且在codesandbox也可以看到(react-hooks/exhaustive-deps)eslint给出的使用useMemo的修改建议。 123456789101112131415161718192021222324import React,{useEffect, useState} from "react";import "./styles.css";const Apple = ({time,count}) => { const options = {time,count} useEffect(()=>{ console.log(`thers is ${options} apples`) },[options]) return( <div>apple</div> )}export default function App() { const [girl,setGirl] = useState(0) const [count,setCount] = useState(0) const [time,setTime] = useState(0) return ( <div className="App"> <h1 onClick={() => setGirl(girl+1)}>{girl}</h1> <h2>Start editing to see some magic happen!</h2> <Apple count={count} time = {time} /> </div> );} 但是参看博客,任何性能优化都具有代价,在进行使用前都应该测量一下,性能优化的代价是不是足以抵销重计算的时间。 useRefuseRef 会在每次渲染时返回同一个 ref 对象,并且变更 .current 属性的变化不会引发组件重新渲染。 官方文档的应用示例:阻止input框不必要的rerender 12345678910111213141516function App() { const [value, setValue] = React.useState(""); const valueRef = React.useRef(); const handleClick = e => { setValue(valueRef.current.value); }; return ( <div className="App"> <h4>Value: {value}</h4> <input ref={valueRef} /> <Button onClick={handleClick}>Button</Button> </div> );}]]></content>
</entry>
<entry>
<title><![CDATA[react in patterns]]></title>
<url>%2F2020%2F09%2F27%2Freact-in-patterns%2F</url>
<content type="text"><![CDATA[react design patterns声明式声明式 vs 命令式123456quicksort :: (Ord a) => [a] -> [a]quicksort [] = []quicksort (x:xs) = let smallerSorted = quicksort [ a | a <- xs , a <= x] biggerSorted = quicksort [a | a <- xs , a > x] in smallerSorted ++ [x] ++ biggerSorted 对于快排,haskell 的 quicksort 每轮递归的描述是:smallerSorted 是由 xs 中小于 x 的数组合而成的数组快排的结果。biggerSorted 是由 xs 中大于 x 的数组合而成的数组快排的结果该轮递归的返回值是 smallerSorted ++ [x] ++ biggerSorted.(三个数组拼接) 123456789101112131415161718192021void quickSort(int s[], int l, int r){ if (l< r) { int i = l, j = r, x = s[l]; while (i < j) { while(i < j && s[j]>= x) // 从右向左找第一个小于x的数 j--; if(i < j) s[i++] = s[j]; while(i < j && s[i]< x) // 从左向右找第一个大于等于x的数 i++; if(i < j) s[j--] = s[i]; } s[i] = x; quickSort(s, l, i - 1); // 递归调用 quickSort(s, i + 1, r); }} 对于cpp 的 quicksort 每轮递归的描述是:(以i作为左指针,以j作为右指针),从r至l找到第一个大于x的数s[j],交换s[i]与s[j]的值,然后从i++至j找到第一个小于x的数s[i],交换s[i]与s[j],然后从j–向i找第一个大于x的数s[j]……(不断重复这个过程直至i、j两指针相遇,将 x 赋值给 s[i]。(一个混乱的非完整描述……) 同样一个分治算法实现的快排,不同的范式语言可以感受到很大的差别,除了代码量的差别外,haskell的快排函数可以用是什么这样的句子解释,cpp的快排函数用的是步骤解释。也就是我们常说的 what 和 how 的差别。 虽然从这个快排的例子上看,declarative 比 imperative 用了更少的代码量,和更简洁的处理逻辑,但是也不意味着声明式能在众多应用情景中优于命令式,有的时候下定义比描述过程更为困难。 react 声明式的体现1234567891011121314151617function addArtistNameToBody() { const bodyTag = document.querySelector('body') const divTag = document.createElement('div') let h1Tag = document.createElement('h1') h1Tag.innerText = "Mitski" divTag.append(h1Tag) bodyTag.append(divTag)}class Artist extends Component { render() { return( <div> <h1>{this.props.name}</h1> </div>) }} 前者体现的dom节点的构建过程,后者体现的组件含有的内容。 函数式个人不完全正确的理解:尽可能的对逻辑块独立的地方进行函数抽取,无论当下复用情况高不高,只要它这部分的代码逻辑相对独立。这样做的好处是具有更明显的抽象层次,使得代码更易读,和代码改动更易进行。纯函数式相对一般的函数式,是不会产生副作用,不会改变函数外部的任何的状态(变量的值)。据我所知(没有经过验证),纯函数式是由于不存在变量,或者说存在一种变量声明即需要初始化,且之后不可更改它的值,所以函数调用是无法产生副作用的,无副作用的强大之处在于代码更可控,意味这你不必去跟踪一个变量在何处被意外的改变等等(不过我也没对纯函数式语言的项目debug过)。react state是变化的,所以react不是纯函数式。当然高阶抽象这样的良好的编程规范,不是函数式语言所特有的,在我们讲面向对象语言的设计模式的时候,讲:单一原则、里式原则(不记得那些名词了,之后补),这些原则也都在强调一件事,逻辑抽象,代码分层提高代码可复用率。在进行抽象后,在不同的抽象层中连接的问题上,个人觉得fp能更简洁轻便地实现,也就是说fp在抽象上更具有优势(原因水平有限还说不清,而且这个blog也不是主讲fp与oop的),看我能不能找到些许例子……oop-learn-about-abstract-form-fp FP vs OOP123456789101112131415161718192021222324252627282930313233343536373839404142434445464748static int compute(int n, Computer computer) { int result = computer.identity(); for (int i = 1; i <= n; i++) { result = computer.compute(result, i); } return result;}interface Computer { int compute(int a, int b); int identity();}static class Adder implements Computer { @Override public int compute(int a, int b) { return a + b; } @Override public int identity() { return 0; }}static class Multiplier implements Computer { @Override public int compute(int a, int b) { return a * b; } @Override public int identity() { return 1; }}public static void main(String... args) { System.out.println(compute(5, new Adder())); System.out.println(compute(5, new Multiplier()));} 1234567891011121314151617@FunctionalInterfaceinterface Computer { int compute(int a, int b);}static int compute(int n, int identity, IntBinaryOperator computer) { int result = identity; for (int i = 1; i <= n; i++) { result = computer.applyAsInt(result, i); } return result;}public static void main(String... args) { System.out.println(compute(5, 0, (a, b) -> a + b)); System.out.println(compute(5, 1, (a, b) -> a * b));} 这两个例子恰巧的说明了面向对象语言也可以用来做函数式编程。显而易见的区别是:fp中不存在子类继承父类,再在子类中定义方法的代码结构,减少了代码的冗余度,代码整体观感更精简。 react 函数式的体现 单向数据流和props不可更改优势: 更容易debug,知道数据的来源 更有效率,让系统知道各部分的边界是什么 根据输入渲染输出 组件可由function定义,并进行组合 why react hooksreact hooks的引入增强了fp。当然hooks通过不使用各种生命周期函数和this简化了代码,但这不是react hooks最神奇的地方,最神奇的地方在于它可以通过custom hook更简便的进一步提取逻辑、进行抽象,减少了hoc,class,function之间的切换。从下面这里例子来看,custom hook 替代hoc实现同样的效果,并且在API上理解也更容易(文档里HOC的限制还挺多了,不过我也没怎么用过HOC)。 If the React community embraces [hooks], it will reduce the number of concepts you need to juggle when writing React applications. Hooks let you always use functions instead of having to constantly switch between functions, classes, higher-order components, and render props. 12345678910111213141516171819202122232425262728293031323334353637383940414243const CommentListWithSubscription = withSubscription( CommentList, (DataSource) => DataSource.getComments());const BlogPostWithSubscription = withSubscription( BlogPost, (DataSource, props) => DataSource.getBlogPost(props.id));// 此函数接收一个组件...function withSubscription(WrappedComponent, selectData) { // ...并返回另一个组件... return class extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); this.state = { data: selectData(DataSource, props) }; } componentDidMount() { // ...负责订阅相关的操作... DataSource.addChangeListener(this.handleChange); } componentWillUnmount() { DataSource.removeChangeListener(this.handleChange); } handleChange() { this.setState({ data: selectData(DataSource, this.props) }); } render() { // ... 并使用新数据渲染被包装的组件! // 请注意,我们可能还会传递其他属性 return <WrappedComponent data={this.state.data} {...this.props} />; } };} 12345678910111213141516171819202122232425function useData(selectData) => { const [data,setDate] = useState(); useEffect( function handleChange(){ setDate( selectData(DataSource, props) ) } DataSource.addChangeListener(handleChange); return DataSource.removeChangeListener(handleChange) ,[]) return data;}const CommentList = () => { const comments = useData((DataSource) => DataSource.getComments()) return( <div> {comments.map((comment) => ( <Comment comment={comment} key={comment.id} /> ))} </div> )}const BlogPost = () => { const blogPost = useData((DataSource, props) => DataSource.getBlogPost(props.id)) return <TextBlock text={blogPost} />;}]]></content>
</entry>
<entry>
<title><![CDATA[Taro H5 项目总结]]></title>
<url>%2F2020%2F05%2F01%2FTaro-H5-%E9%A1%B9%E7%9B%AE%E6%80%BB%E7%BB%93%2F</url>
<content type="text"><![CDATA[项目建立h5项目选用taro框架感觉不太好,很多API是在小程序环境得到支持的,在h5端的支持度都没有写明可不可以用,比如123npm install -g @tarojs/clitaro init myAppnpm run dev:h5可能会出现的问题,network_error 服务器错误,记得把fan qiang软件的全局模式切掉。这个问题貌似只在初次dev的时候出现过,之后开全局也能正常dev,之后好好学计网吧,现在我也不太懂.npm run dev:h5后可以看到: 况且taro中封装了太多东西,一方面可以提高生产效率,但另外一方面也降低了对技术的要求,比如了解web API的使用,比如规范化css类名,比如构建页面路由,比如http request。 当你在本地改了代码甚至不需要手动刷新浏览器,就可以看到更改,这依赖于wepack HMR功能。 当你ctrl + c退出后,可以看到如上,使用 HMR 的过程中,(有人说)通过 Chrome 开发者工具知道浏览器是通过websocket和webpack-dev-server 进行通信的,感觉webpack-dev-server是一个很值得研究的东西。 定位+位置比对使用navigator.geolocation.getCurrentPosition web API 获取经纬度,不同浏览器可能使用的地理信息服务,通过调试可知,qq浏览器使用的是google服务。然而google位置获取服务已经不再支持http了…… 项目需求的情景是给用户推荐附近食堂菜单,所以必然要涉及到距离测算。这里我使用腾讯位置服务其中有两个比较难搞的点。 MD5加密在进行配置的时候,可以看到如下,如果使用的是签名校验验证方法,则会涉及md5加密算法。 md5并不是js内置的方法,所以要外部引入,引入可以用npm install相关包,也可以直接copy 源代码。js版本的md5在github上也挺好找的。做适量更改。 12345//md5.min.jsexport default md5//index.jsximport md5 from './md5.min.js';let sig = md5('/ws/distance/v1/?from=28.983171,116.308479&key=BQBBZ-TRBCW-J3WRO-OUTER-XXXXXX&mode=driving&to=28.996060,116.353455;28.949227,116.3943108kjYTMrC9zaOW9xTLS5N3EnB7HWviv'); 建议在md5.min.jsrun得到sig后在postman测试一下。排除一下配置错误,当然你之后就会发现会在浏览器中碰到postman中不会有的跨域问题,嘿嘿嘿😁…… webServerce API 跨域限制 如error所示,No ‘Access-Control-Allow-Origin’ header is present on the requested resource.在项目中使用 request 调用腾讯接口,会得到类似的错误,然而这是第三方服务,所以无法做到让服务器在响应首部中添加该字段。注意到该接口接受到JSONP回调。 使用script标签调用接口后会返回一段js代码。在下面的情景中,则是接口的 responce 会成为globalFun的参数,返回 globalFun(res) 这个函数,实际上就将这个函数执行了。记得要将回调函数 globalFun 定义在全局可访问的位置。不然只定义在调用接口的页面,你会发现 undefined 的报错。 123456789101112131415161718192021222324252627282930313233// src/index.html<script> function globalFun(obj){ console.log("globalfun"); console.log(obj); console.log(obj.result.elements); let res = JSON.stringify(obj); window.localStorage.setItem('resJson', res); }</script>// components/page/shake/index.jsxuseWebService(pos){ console.log('useWebService'); let sig = md5('/ws/distance/v1/?' +'callback=globalFun' +'&from='+pos.coords.latitude+','+pos.coords.longitude +'&key=BQBBZ-TRBCW-J3WRO-OUTER-------'+'&mode=walking' +'&output=jsonp' +'&to=28.086060,115.053455;28.069227,116.044310' +'8kjYTMrC9zaOW9xTLS5N------') const script = document.createElement("script"); script.src = 'https://apis.map.qq.com/ws/distance/v1/?' +'callback=globalFun' +'&from='+pos.coords.latitude+','+pos.coords.longitude +'&key=BQBBZ-TRBCW-J3WRO-OUTER------'+'&mode=walking' +'&output=jsonp' +'&to=28.086060,115.053455;28.069227,116.044310' +'&sig='+sig; script.async = false; document.body.appendChild(script);} 摇动监测 调用Taro系统信息查询API 如果是iOS设备,调用devicemotion Permission 接口请求获得许可 监测摇动,我使用的是github中一个shake插件 部分代码如下: 1234567891011121314151617181920212223242526272829303132// shake.js 不推荐copy源文件引入 我存粹图方便export default Shake;// index.jsxcomponentDidMount() { const system = Taro.getStorageSync('system'); if(system == "iOS"){ if(typeof(DeviceMotionEvent) !== 'undefined' &&typeof DeviceMotionEvent.requestPermission === "function"){ DeviceMotionEvent.requestPermission() .then(response => { alert("Orientation tracking " + response); if (response == "granted") { // window.addEventListener("devicemotion", e => { // //getNearby事件触发条件 // // document.getElementById("request").style.visibility = "hidden"; // }); var myShakeEvent = new Shake(); myShakeEvent.start(); window.addEventListener('shake', this.shakeEventDidOccur, false); }else{ alert('您已拒绝请求,将无法获得摇一摇响应,可重新载入页面,允许请求~') } }) .catch(console.error); } }else{ var myShakeEvent = new Shake(); myShakeEvent.start(); window.addEventListener('shake', this.shakeEventDidOccur, false); }} 项目调试本地跑项目,直接在浏览器中使用开发者工具即可。若想要真机调试,推荐使用Chrome远程调试。 如果需要对devServer环境进行配置,参照webpack devServer 配置,可按如下:配置了本地可信任ssl证书,可其实对我想获得https信赖来调试定位起不到作用,因为本地信任,而做不到公网信任啊……所以只能用github pages 生成预览页面,抽出来测试需要https的部分。 12345678910111213141516171819202122// config/index.js// 此处应注意config中的 module 语法规范和 src 中的规范不一样的问题var fs = require('fs');h5: { devServer:{ https:{ key: fs.readFileSync('/Users/mac/Desktop/example-cert/server.key'), cert: fs.readFileSync('/Users/mac/Desktop/example-cert/server.crt'), ca: fs.readFileSync('/Users/mac/Desktop/example-cert/rootCA.pem'), } // https: true host: "localhost", port:10086, proxy:{ '/api':{ target:'http://food.test.muxixyz.com/', changeOrigin:'true' } } },} ECS+Nginx部署部署步骤待更新,先说一个避坑地方:nginx 更新部署失败(视觉上的失败……) 问题情景:初次部署后,更改代码后,拉取了新代码,重启nginx后,打开浏览器还是没有更新,后面发现是浏览器缓存的问题,请求的时候返回的是304。(但其实有ETag等一些验证机制在,按理不会出现使用不新鲜缓存的问题,具体原因尚不明白)。 解决办法:勾选 disable cache 选项,强制请求服务器,而不使用缓存。]]></content>
<categories>
<category>项目总结</category>
</categories>
<tags>
<tag>Taro</tag>
<tag>定位</tag>
<tag>摇一摇</tag>
</tags>
</entry>
<entry>
<title><![CDATA[JS 异步(二)]]></title>
<url>%2F2020%2F03%2F08%2FJS-%E5%BC%82%E6%AD%A5-%E4%BA%8C%EF%BC%89%2F</url>
<content type="text"><![CDATA[事件循环和任务队列在 ES6 中,有一个新的概念建立在事件循环队列之上,叫作任务队列(job queue)。这个概念给大家带来的最大影响可能是 Promise 的异步特性(参见第 3 章).事件循环队列类似于一个游乐园游戏:玩过了一个游戏之后,你需要重新到队尾排队才能 再玩一次。而任务队列类似于玩过了游戏之后,插队接着继续玩. 回调导致的信任问题 回调未调用 多次回调 吞掉错误或异常 等等 关于回调123456789101112131415161718//这个叫做回调函数,可是并不异步function useless(callback){ return callback();}useless(()=>{console.log("callback")});(function fnA(){ console.log("fnA");})()//异步上的回调document.body.addEvetListener("mousemove",function(){ //})//node的内部模块的异步回调惯例const fs = require('fs');fs.readFile('./title.json',(err,data)=>{ if(err) throw err; //}) 交付第三方工具后控制反转有时候 ajax(..)(也 就是你交付回调 continuation 的第三方)不是你编写的代码,也不在你的直接控制下。多 数情况下,它是某个第三方提供的工具。我们把这称为控制反转(inversion of control),也就是把自己程序一部分的执行控制交给某 个第三方。 123456789101112131415//Aajax('..',function(..){ //C});//B//具体情景,//var tracked = false;analytics.trackPurchase(purchaseData, function(){ //if(!track) chargeCreditCard(); displayThankyouPage();});//课栈遇到的回调未执行的情况…… 分析公司的开发者开发了一些实验性的代码,在某种情况下,会在五秒钟内每秒重试一次传入的回调函数,然后才会因超时而失败。 异步开发的难题 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465//手动维护一个任务队列的执行//串行流程控制//first task 1000setTimeout(function() { //second task 500 console.log('I execute first.'); setTimeout(function() { console.log('I execute next.'); setTimeout(function() { console.log('I execute last.'); }, 100); }, 500);}, 1000);//并行流程控制,输出某些单词在整个文件目录下出现次数var fs = require('fs');var completedTasks = 0;var tasks = [];var wordCounts = {};var filesDir = './text';function checkIfComplete() { completedTasks++; if (completedTasks == tasks.length) { for (var index in wordCounts) { console.log(index +': ' + wordCounts[index]); } }}function countWordsInText(text) { var words = text .toString() .toLowerCase() .split(/\W+/) .sort(); for (var index in words) { var word = words[index]; if (word) { wordCounts[word] = (wordCounts[word]) ? wordCounts[word] + 1 : 1; } }}fs.readdir(filesDir, function(err, files) { if (err) throw err; for(var index in files) { //用了闭包,冻结file[index]的值 var task = (function(file) { return function() { fs.readFile(file, function(err, text) { if (err) throw err; countWordsInText(text); //每次完成后一个task,都进行任务检查 checkIfComplete(); }); } })(filesDir + '/' + files[index]); //依次添加至任务队列 tasks.push(task); } for(var task in tasks) { //用循环执行,readfile是非阻塞I/O tasks[task](); }}); Promise 特点 promise异步是基于微任务的 ->回调太晚(等待时间太长) promise一旦决议,就永远保持这个状态 决议机制 ->避免控制反转,回调未调用,回调多次 实用 链式回调 ->串行控制 promise.race ->可以用来处理一直未决议的状况 promise.all ->并行控制 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758const promise = new Promise((resolve, reject) => { resolve("Hattori"); setTimeout(()=> reject("Yoshi"), 500);});promise.then(val => alert("Success: " + val)) .catch(e => alert("Error: " + e));//Success: Hattori//实际开发 upload() { return new Promise(resolve => { Taro.uploadFile({ url: 'https://kstack.test.muxixyz.com/api/v1/upload/image/', //上传头像的服务器接口 filePath: this.state.avatar, name: 'image', formData: { // image: this.state.file }, header: { token: Taro.getStorageSync('token') // 'content-type': 'multipart/form-data' }, success(res) { if (res.data) { resolve(JSON.parse(res.data).data.url); } } }); }); } onSubmit() { if (this.state.username == '') { Taro.atMessage({ message: '标题不能为空', type: 'warning' }); return; } this.upload() .then(url => { Fetch( 'api/v1/user/info/', { username: this.state.username, avatar: url }, 'POST' ).then(ress => { if (ress.message == 'OK') Taro.showToast({ title: '修改成功', icon: 'success' }); }); }) .catch(err => { console.error(err); Taro.showToast({ title: '修改失败,请稍后重试', icon: 'fail' }); }); } thenable具有then()函数的对象,都是promise吗?不是。普通then()与promise产生的then的区别 123456789101112131415var p = { then: function(cb,errb){ cb(42); errb("evil laugh"); }};p.then( function fulfilled(val){ console.log(val); //42 }, function rejected(err){ console.log(err); //evil laugh //如果是真正promise的then方法,这两个是不会都运行的 }) Promise与生成器结合生成器函数是一个崭新的函数类型,它和标准的普通函数完全不同。生成器函数能生成一组值的序列,但是每组值的生产都是基于每次请求,并不同于标准函数那样立即生成。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051//能够暂停是由于函数上下文的保存function *IdGenerator(){ let id = 0; while(true){ yield ++id; }}const idIterator = IdGenerator();const ninja1 = { id: idIterator.next().value };assert(ninja1.id === 1, "First ninja has id 1");//combinationfunction getJSON(url){ return new Promise((resolve, reject) =>{ const request = new XMLHttpRequest(); request.open("GET", url); request.send(); request.onload = function() { try{ if(this.status === 200){ resolve(JSON.parse(this.response)) } //else } catch(e){ reject(e.message) } } //request.onerror })} async(function*() { try { const ninjas = yield getJSON("data/ninjas.json"); const missions = yield getJSON(ninjas[0].missionsUrl); //All information recieved } catch(e) { //An error has occurred }});// ->(async function () { try { const ninjas = await getJSON("data/ninjas.json"); const missions = await getJSON(missions[0].missionsUrl); console.log(missions); } catch(e) { console.log("Error: ", e); }})() 使用生成器与promise结合,串行化异步操作似乎比promise.then().then()这种链式回调更美观,可是我没有看到实质上不得不用asnyc,await的原因。之后再看看……]]></content>
<categories>
<category>note</category>
</categories>
<tags>
<tag>JS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Mastering layout]]></title>
<url>%2F2020%2F02%2F08%2FMastering-layout%2F</url>
<content type="text"><![CDATA[Flex123456789101112131415161718.site-nav > li + li{ margin-left: 1.5em;}.site-nav > .nav-right{ margin-left: auto;}.column-main{ flex: 2; /* flex-grow: 2; */ /* flex-shrink: 1; */ /* flex-basis: 0%; */}.login-form input:not([type=checkbox]):not([type=radio]){ display:block; width:100%;} property justify-content(horizonal) align-items(vertical) align-content(wrap) align-self flexbugs autoprefixer automatiealy creates column of equal height Grid property grid line grid track grid cell grid area naming grid line naming grid area auto grid-auto-rows auto-fill auto-fit object-fit (image) 1234567891011121314151617181920212223.container{ grid-template-rows: repeat(4,auto)}header,nav{ grid-column: 1 / 3; grid-row: span 1;}.portfolio{ display:grid; grid-template-rows: repeat(auto-fill,minmax(200px,1fr)); grid-auto-rows: 1fr; grid-gap: 1em; grid-auto-flow: dense;}.portfolio .featured{ grid-row: span 2; grid-column: span 2;}@suports (display: grid){ ...} position and stacking contexts not positioned static positioned fixed viewport Tip:use it for modal dialogs absolute the closest positioned ancenstor element Tip: dropdown menus,tooltips,dynamic interactions relative relative to where they where unlike fixed and absulte,you can’t use top,right,bottom,left to change size tip:It is usually used to set absolute container as relative sticky scroll normal untill reach some place remember parent must be higher than sticky element tip: is surpported css triangle css-tricky stacking order z-index can’t over ancenstor order careful use,or by custom properties]]></content>
<categories>
<category>note</category>
</categories>
<tags>
<tag>CSS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[CSS In Depth]]></title>
<url>%2F2020%2F01%2F28%2FCSS-In-Depth%2F</url>
<content type="text"><![CDATA[reviewing the fundamentalcascade,specificity and inheritancecascedekeep selector under conrtol stylesheep origin user agent style author author important specificity inline-style(HTML) selector specificity selector IDs classed tags notation html body header h1 0 0 4 0,0,4 #page-title 1 0 0 1,0,0 source order(CSS) inherit,initial(hard-reset)Don’t confuse cascade with inheritance shortand properitiesStay out of trouble with shortand properies Working with relative unitsTip:use rems for font-size,pixels for borders,and ems for most other properties em,rem12345678910.padded{ font-size: 16px; padding: 1em;}:root{ font-size: initial;}.box{ font-size: 1.2rem;} viewport-relative viewport-relative vh:1/100th of the viewport height vw:1/100th of the viewport width vmin:1/100th of the smaller dimemsion,height or width vmax custom properties(aka css variables)caniusecaniuse 123456:root{ --main-font: Helvetica,Arial,sans-serif;}p{ font-family: var(--main-font,blue);} unitless valususe unitless valus when specifying line height 1234567body{ line-height: 1.2}.about-us{ font-size: 2em; /* line-height: 2em*1.2; */} mastering the box model1234567891011121314151617:root{ box-sizing:border-box;}*,::before,::after{ box-sizing:inherit;}.main{ width:70%; float: left;}.sidebar{ width:calc(30%-1.5em); margin-left:1.5em;} nerver explicitly set the height of an element difficuties with element height,columns of equal height 123456789101112131415161718.wrapper{ margin-left: -1.5em; margin-right: -1.5em;}.container{ display:table; width:100%; /* margin can't work */ border-spacing:1.5em 0;}.main{ display: table-cell; width:70%;}.sidebar{ display: table-cell; width:30%;} how to center table-cell,vertical-align flex absulute positioning line-height collapsed marginmargin collapsing only occurs with top and bottom collapsing between text collapsing multiple margin collapsing ouside a container mastering layoutmastering floatTip: By using max-width instead of width to avoid horizontal scrolling on the devies with smaller screens. clear float123456789101112131415161718.clear{ clear: both;}.clearfix::after{ display: block; content: " "; clear: both;}/* prevent margin collapse *//* with ::after you can insert an element into the DOM at the end of container */.clearfix::before,.clearfix::after{ display: table; content: " ";}.clearfix::after{ clear: both;} Establishing a block formatting contextit isolates its contents from the outside context) BFC Page:103 float:left,float:right overflow:hidden,auto,scroll, display:inline-block position: absolute or fixed flexboxgrid layout]]></content>
<categories>
<category>note</category>
</categories>
<tags>
<tag>CSS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[JS 异步]]></title>
<url>%2F2020%2F01%2F23%2FJS-%E5%BC%82%E6%AD%A5%2F</url>
<content type="text"><![CDATA[什么是异步12setTimeout(()=>{console.log("路")},1000);console.log("遥"); // 遥路在1000s的时间(需要较长时间响应的事件)内执行了下一块代码。正确说法:任何时候,只要把一段代码包装成一个函数,并指定它在响应某个事件(定时器、鼠标点 击、Ajax 响应等)时执行,你就是在代码中创建了一个将来执行的块,也由此在这个程序 中引入了异步机制。 传统的实现异步的方式 回调 其他 为什么我们会需要使用异步,因为我们不想浏览器发生阻塞,想象我们手机卡住的场景,这简直难以忍受;但是通过回调引发的异步同样会引发新的问题(当然,这些问题与引起的阻塞相比都是小问题)。(其中影响最大的“信任问题”。) 回调引发的问题顺序问题->追踪难度加大12345678doA(function(){ doC(); doD(function(){ doF(); }); doE();} );doB(); 或许能够追踪到A → B → C → D → E → F这样的执行顺序,可是真正的异步实际情况会复杂很多。而且在某些情况下,doA顺序执行了,而在你的代码中,函数的执行结果很依赖于各个函数的执行顺序,这时候你很难追踪到是那个部分出了问题。当然也可以通过硬编码(调整函数代码块的位置和设置if语句)去排除这些问题,但是这样就将代码复杂化了。所以我们需要更好的异步模式。 信任问题12345//Aajax("..",function(...){ //C});//B 有时候 ajax(..)(也 就是你交付回调 continuation 的第三方)不是你编写的代码,也不在你的直接控制下。多数情况下,它是某个第三方提供的工具。我们把这称为控制反转(inversion of control),也就是把自己程序一部分的执行控制交给某个第三方。(出现的不可信任的第三方)。由于某些原因,c没有回调成功,执行ajax这个工具进行了会在五秒钟内每秒重试一次传入的回调函数,然后才会因超时而失败。之后这5个回调成功了,c被执行了5次。如果c是一个支付相关的,则用户会被扣款五次。当然这也可以通过设置if条件,防止第三方工具瞎搞。出了重复回调外,还会有其他问题 调用回调过早(在追踪之前) 调用回调过晚(或没有调用) 调用回调的次数太少或太多(就像你遇到过的问题!) 没有把所需的环境 / 参数成功传给你的回调函数 吞掉可能出现的错误或异常 为了解决这一系例隐患,我们不知道要增加多少工作量,所以我们迫切需要新的异步模式! 新的异步模式Promise在 ES6 中,有一个新的概念建立在事件循环队列之上,叫作任务队列(job queue)。事件循环队列类似于一个游乐园游戏:玩过了一个游戏之后,你需要重新到队尾排队才能 再玩一次。而任务队列类似于玩过了游戏之后,插队接着继续玩。一个 Promise (p1)决议后,这个 Promise 上所有的通过 then(..) 注册的回调(cb1,cb2)都会在下一个异步时机点(p2 cb3)上依次被立即调用。这些回调中的任意一个(cb1)都无法影响或延误对其他回调(cb2)的调用。由Promise创建的then异步是基于任务的,而不像cb是基于事件的。 什么是Promise promise作为一个返回值 1234567891011function foo(x){ //构造并返回一个promise return new Promise( function(resolve,reject){ //promise的决议回调 } )}//new Promise( function(..){ .. } )模式//? promise事件 123456789101112131415//相比回调,实现了控制反转,bar不再通过foo的成功调用var p = foo(2);bar(p);baz(p);function bar(fooPromise){ fooPromise.then( function(){ //foo success }, function(){ //foo err } );}//baz 同 理解then(…)具有then()函数的对象,都是promise吗?不是。普通then()与promise产生的then的区别? 123456789101112131415var p = { then: function(cb,errb){ cb(42); errb("evil laugh"); }};p.then( function fulfilled(val){ console.log(val); //42 }, function rejected(err){ console.log(err); //evil laugh //如果是真正promise的then方法,这两个是不会都运行的 }) Promise如何解决了之前的问题,你看出来了吗关于信任问题 12345678910111213//Promise 构造器var p = new Promise( function(resolve,reject){ resolve(42); reject();})p.then( function fulfill(x1){ console.log(x1);//完成后 42 }, function reject(x2){ console.err(x2); }) 回调未调用reject事件但如果Promise永远不被决议呢,Promise提供了解决方案,一种称为竞态的解决方案 调用次数过多或过少由于 Promise 只能被决议一次,且决议后的结果不会被更改,所以任何通过 then(..) 注册的(每个)回调就只会被调用一次。如果你把同一个回调注册了不止一次(比如p.then(f); p.then(f);),那它被调用的次数就会和注册次数相同 调用过晚在下一个异步点之前一定会结束这个任务。 未能传递参数/环境值依靠注册的回调传递 吞掉错误或异常reject能捕捉到 123456789101112var p = new Promise( function(resolve,reject){ foo.bar(); //foo undefined resolve(42);} )p.then( function fufilled(){ //will not reach here }, function rejected(err){ // err将会是一个TypeError异常对象来自foo.bar()这一行 }) 如何将传统的回调异步转换为Promise异步模式12345678910111213141516171819202122232425262728293031323334353637383940//回调function add(getX,getY,cb){ var x,y; getX(function(xval){ x = xval; if(y != undefined){ cb(x+y); } })//这种写法?getX里面是一个函数?莫非是高阶函数?xval应该是fetchX的返回结果 getY(function(yVal){ y =yVal; if(x != undifined){ cb( x+y); } })}add(fetchX,fetchY,function(sum){ console,log(sum);})//Promise模式function add(xPromise,yPromise) { return Promise.all([xPromise,yPromise]).then( function(valus){ //.then会返回一个promise,这个promise由return values[0] + values[1]这一 行立即决议(得到加运算的结果) return values[0]+values[1]; } //fulfill(values[0]+values[1]); )}add( fetchX(),fetchY() ).then( function(sum){ console.log( sum ); }, function(err){ console.error(err); });//通过Promise API学习使用 Promise的不足其一为了避免丢失被忽略和抛弃的 Promise 错误,一些开发者表示,Promise 链的一个最佳实践 就是最后总以一个 catch(..) 结束,比如 123456789var p = Promise.resolve(42);p.then( function fufill(msg){ // 数字没有string函数,所以会抛出错误 console.log( msg.toLowerCase()) }).catch( handleErrors);//如果 handleErrors(..) 本身内部也有错误怎么办呢?谁来捕捉它?]]></content>
<categories>
<category>note</category>
</categories>
<tags>
<tag>JS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[this]]></title>
<url>%2F2019%2F11%2F22%2Fthis%2F</url>
<content type="text"><![CDATA[绑定规则找到调用位置后,我们还需根据绑定规则确定 this 的绑定对象。默认绑定最常用的函数调用类型:独立函数调用。无法应用其他规则的默认规则。12345function foo() { console.log(this.a);}var a = 2;foo(); //2在代码中,foo() 是直接使用不带任何修饰的函数引用进行调用的,因此只能使用 默认绑定,无法应用其他规则。如果使用严格模式(strict mode),那么全局对象将无法使用默认绑定,因此 this 会绑定 到 undefined;严格模式下与 foo() 的调用位置无关: 123456789function foo() { console.log(this.a);}var a = 2;(function() { "use strict"; foo(); // 2})();//不过在代码中混用严格模式和非严格模式的行为是不可取的。 隐式绑定另一条需要考虑的规则是调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含,不过这种说法可能会造成一些误导。 12345678function foo() { console.log(this.a);}var obj = { a: 2, foo: foo};obj.foo(); 调用位置会使用 obj 上下文来引用函数,因此你可以说函数被调用时 obj 对象“拥 有”或者“包含”它。当函数引用有上下文对象时,隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象。因为调用 foo() 时 this 被绑定到 obj,因此 this.a 和 obj.a 是一样的。 隐式丢失指的是被隐式绑定的函数丢失绑定对象,而应用默认绑定的情形。而默认绑定把 this 绑定到全局对象或者 undifined 上,取决于是否为严格模式。 1234567891011function (){ console.log(this.a);}var obj = { a:2; foo:foo;}var bar = obj.foo; //函数别名var a ="global"bar(); //"global" 虽然 bar 是 obj.foo 的一个引用,但是实际上,它引用的是 foo 函数本身,因此此时的 bar() 其实是一个不带任何修饰的函数调用,因此应用了默认绑定。 显式绑定如果我们不想在对象内部包含函数引用,而想在某个对象上强制调用函数,该怎么做呢?可以使用函数的 call(..) 和 apply(..) 方法。 1234567function foo() { console.log(this.a);}var obj = { a: 2};foo.call(obj); //2 通过 foo.call(..),我们可以在调用 foo 时强制把它的 this 绑定到 obj 上。 硬绑定显式绑定仍然无法解决我们之前提出的丢失绑定问题。(硬绑定例子对比那隐式丢失的例子)。但是显式绑定的一个变种可以解决这个问题。 1234567891011121314function foo() { console.log(this.a);}var obj = { a: 2};var bar = function() { foo.call(obj);};bar(); //2//硬绑定bar不能修改foo的this指向了bar.call(window); //2 由于硬绑定是一种非常常用的模式,所以在 ES5 中提供了内置的方法 Function.prototype. bind,它的用法如下: 12345678910function foo(something) { console.log( this.a, something ); return this.a + something;} var obj = { a:2};var bar = foo.bind( obj );var b = bar( 3 ); // 2 3 console.log( b ); // 5 bind(..) 会返回一个硬编码的新函数,它会把参数设置为 this 的上下文并调用原始函数。 new 绑定使用 new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。 创建(或者说构造)一个全新的对象。 这个新对象会被执行[[ 原型 ]]连接。 这个新对象会绑定到函数调用的 this。 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。 12345function foo(a) { this.a = a;}var bar = new foo(2);console.log(bar.a); //2 使用 new 来调用 foo(..) 时,我们会构造一个新对象(_这个新对象名为 bar_)并把它绑定到 foo(..) 调用中的 this 上。 绑定规则的优先级当两种绑定同时出现时,我们则需要比较它们的优先级 123456789101112131415function foo() { console.log(this.a);}var obj1 = { a: 2, foo: foo};var obj2 = { a: 3, foo: foo};obj1.foo.call(obj2); // 3obj2.foo.call(obj1); // 2//可以看到,显式绑定优先级更高 函数是否在 new 中调用(new 绑定)?如果是的话 this 绑定的是新创建的对象。var bar = new foo() 函数是否通过 call、apply(显式绑定)或者硬绑定调用?如果是的话,this 绑定的是 指定的对象。var bar = foo.call(obj2) 函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this 绑定的是那个上 下文对象。var bar = obj1.foo() 如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到 undefined,否则绑定到 全局对象。var bar = foo() 箭头函数的 this 指向箭头函数不使用 this 的四种标准规则,而是根据外层(函数或者全局)作用域来决 定 this。 1234567891011121314function foo(){ return (a) => { console.log(this.a); };}var obj1 ={ a:2;}var obj2 ={ a:3;}var bar = foo.call(obj);bar.call(obj2); //2 foo() 内部创建的箭头函数会捕获调用时 foo() 的 this。由于 foo() 的 this 绑定到 obj1, bar(引用箭头函数)的 this 也会绑定到 obj1,箭头函数的绑定无法被修改。(new 也不 行!) 箭头函数可以像 bind(..) 一样确保函数的 this 被绑定到指定对象,此外,其重要性还体 现在它用更常见的词法作用域取代了传统的 this 机制。实际上,在 ES6 之前我们就已经 在使用一种几乎和箭头函数完全一样的模式。 123456789101112131415161718192021function foo() { setTimeout(() => {// 这里的 this 在此法上继承自 foo() console.log( this.a ); },100);}var obj = { a:2};foo.call( obj ); // 2function foo() { var self = this; // lexical capture of this setTimeout( function(){ console.log( self.a ); }, 100 );}var obj = { a: 2};foo.call( obj ); // 2 虽然 self = this 和箭头函数看起来都可以取代 bind(..),但是从本质上来说,它们想替 代的是 this 机制。如果你经常编写 this 风格的代码,但是绝大部分时候都会使用 self = this 或者箭头函数 来否定 this 机制,那你或许应当: 只使用词法作用域并完全抛弃错误this风格的代码; 完全采用 this 风格,在必要时使用 bind(..),尽量避免使用 self = this 和箭头函数.]]></content>
<categories>
<category>note</category>
</categories>
<tags>
<tag>JS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[作用域]]></title>
<url>%2F2019%2F11%2F13%2F%E4%BD%9C%E7%94%A8%E5%9F%9F%2F</url>
<content type="text"><![CDATA[动态作用域与词法作用域123456789function foo() { console.log( a );}function bar() { var a = 3; foo();}var a = 2; bar();//什么状况,为什么是2,而不是undifined?啊,我知道了,上面那条是由于a=2留在原地等待执行,console执行在前,在此例中执行bar执行在后,不是说关注声明么,是的,关注声明只是让你找对容器,而容器里装什么与声明无关,所以并不矛盾。 如果在你看来,此处应该输出2(当然此处涉及到对于变量提升的理解),则说明你关注的是foo()的声明;如果你认为会输出3,则你关注的是函数的调用。借此例引出词法作用域和动态作用域的主要区别: 词法作用域是在写代码或者说定义时确定的,而动态作用域是在运行时确定 的。(this 也是!)词法作用域关注函数在何处声明,而动态作用域关注函数从何处调用。 如何理解运行时确定这个概念,bar()调用foo(),动态生成调用栈,基于调用栈生成作用域链,而不是基于代码中的作用域嵌套生成作用域链(词法作用域的做法)。 对于变量的查找,js是遵循词法作用域的,并且现在的大多数语言都是遵循词法作用域的。为什么js不使用动态作用域?我觉得一方面是因为,静态的更好控制,动态的不好控制。javascript引擎会在在编译阶段进行数项性能的优化。其中一些优化依赖于能够根据代码的词法进行静态的分析,并预先确定所有变量和函数的定义位置,才能在执行过程中快速找到标识符。 作用域嵌套当一个块或函数嵌套在另一个块或函数中时,就发生了作用域的嵌套。因此,在当前作用 域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量, 或抵达最外层的作用域(也就是全局作用域)为止。 12345678//作用域嵌套实例function bar() { var a = 3; (function foo() { console.log( a ); })()}var a = 2; bar();//3 块级作用域函数作用域和全局作用域,都很好观察和理解。那什么是块级作用域?let 关键字可以将变量绑定到所在的任意作用域中(通常是{ .. }内部)。换句话说let为其声明的变量隐式地了所在的块作用域。同样的,const声明也可以创建块级作用域。 123456789101112131415161718//隐式创建块作用域var foo = true;if(foo){ let bar = foo*2; console.log(bar);}console.log(bar);//2 referrence error//显示的创建块作用域var foo = true;if(foo){ { let bar = foo*2; console.log(bar); }}console.log(bar);//2 referrence error 推荐使用显示创建块作用域。在这个例子中,我们在 if 声明内部显式地创建了一个块,如果需要对其进行重 构,整个块都可以被方便地移动而不会对外部 if 声明的位置和语义产生任何影响。 变量提升123456789101112//例1console.log(a);var a=2;//undifined//例2a=2;var a;console.log(a);//2//无关紧要的例3,非严格模式下的LSH查找,未查找到a的声明,会自动创建一个a的声明;a=2;console.log(a);//2 按照结果逆推,可以感觉到变量被声明到了顶部。实际上也是:引擎会在解释JavaScript代码之前首先对其进行编译。编译阶段中的一部分工作就是找到所有的声明,并用合适的作用域将它们关联起来。(所以函数声明也能被提升)值得注意的是:第一个定义声明是在编译阶段进行的。第二个赋值声明会被留在原地等待执行阶段。所以是undifinded。 闭包闭包是基于词法作用域书写代码时所产生的自然结果,你甚至不需要为了利用它们而有意 识地创建闭包。闭包的创建和使用在你的代码中随处可见。你缺少的是根据你自己的意愿来识别、拥抱和影响闭包的思维环境。 定义:当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。 123456789function foo(){ var a = 2; function bar(){ console.log(a); }; bar();}foo(); //2 技术上来讲,也许是。但根据前面的定义,确切地说并不是。我认为最准确地用来解释 bar() 对 a 的引用的方法是词法作用域的查找规则,而这些规则只是闭包的一部分。(但却 是非常重要的一部分!)函数 bar() 具有一个涵盖 foo() 作用域的闭包。但是通过这种方式定义的闭包并不能直接进行观察,也无法明白在这个代码片段中闭包是 如何工作的。我们可以很容易地理解词法作用域,而闭包则隐藏在代码之后的神秘阴影 里,并不那么容易理解。 123456789function foo(){ var a = 2; function bar(){ console.log(a); } return bar;}var baz = foo(); //2 函数 bar() 的词法作用域能够访问 foo() 的内部作用域。然后我们将 bar() 函数本身当作 一个值类型进行传递。在这个例子中,我们将 bar 所引用的函数对象本身当作返回值。在 foo() 执行后,其返回值(也就是内部的 bar() 函数)赋值给变量 baz 并调用 baz(),实 际上只是通过不同的标识符引用调用了内部的函数 bar()。bar() 显然可以被正常执行。但是在这个例子中,它在自己定义的词法作用域以外的地方执行。在 foo() 执行后,通常会期待 foo() 的整个内部作用域都被销毁,因为我们知道引擎有垃 圾回收器用来释放不再使用的内存空间。由于看上去 foo() 的内容不会再被使用,所以很 自然地会考虑对其进行回收。上一个作用域嵌套实现的闭包,foo()的内部作用域会被销毁而闭包的“神奇”之处正是可以阻止这件事情的发生。事实上内部作用域依然存在,因此 没有被回收。谁在使用这个内部作用域?原来是 bar() 本身在使用。拜 bar() 所声明的位置所赐,它拥有涵盖 foo() 内部作用域的闭包,使得该作用域能够一 直存活,以供 bar() 在之后任何时间进行引用。bar() 依然持有对该作用域的引用,而这个引用就叫作闭包。]]></content>
<categories>
<category>note</category>
</categories>
<tags>
<tag>JS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[函数调用栈]]></title>
<url>%2F2019%2F09%2F18%2F%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8%E6%A0%88%2F</url>
<content type="text"><![CDATA[不同语言之间的转换 高级程序语言经过编译,变成汇编代码,汇编代码经过汇编器和连接器变成可执行的机器代码。汇编代码是人类可读的代码。机器代码是01序列。 在程序执行前,数据和指令事先存放在存储器中,每条指令和每个数据都有地址。程序启动后,计算机取指令执行。执行过程中,指令和数据从存储器取到CPU,存放到CPU寄存器中,CPU内的寄存器与控制器与ALU进行数据传送与运算,将运算结果写入内存中(存储器)。 寄存器IA-32体系中,有8个通用寄存器GPR,2个专用寄存器EIP和EFLAGs和六个段寄存器,间接给出段基址。 指令 计算机的指令有微指令、机器指令和伪指令之分。汇编指令是机器指令的符号表示,以帮助记忆,可以称之为助记符。机器指令和汇编指令是一一对应的,都是机器级指令。 机器指令对源操作数进行操作,操作结果储存到目的操作数中。操作码:操作性质;立即数:操作数在指令中给出,这个数叫做立即数。找到操作数或操作数的地址这个过程叫寻址,操作数在不同的位置,有不同的寻址方式。 将cl寄存器中的内容传送到该地址的存储单元中。 指令可以分为传送指令,如mov,push,pop,leal,in,out;定点算术运算指令,如add,sub,int,dec,neg,cmp;浮点运算指令等等。 函数调用过程一段c语言代码12345678910int add(int x,int y){ return x+y;}int main(){ int t1 = 125; int t2 = 80; int sum = add(t1,t2); return sum;} 过程描述存放参数t1,t2;调出add执行,取出参数t1,t2,执行运算,保存返回结果,返回到main中。过程调用执行步骤(p为调用者,Q为被调用者) 关于Q过程中的保存P的现场和恢复P的现场为什么要压入返回地址啊?返回地址是指被调用函数的下一条语句。ret指令会将返回地址送到eip寄存器,即指令指针。 保存现场即保存寄存器,以便被调用者返回前恢复寄存器。根据IA-32的寄存器使用约定: 调用者保存寄存器:eax,edx,ecx如果调用函数P调用完Q返回后,还需用到eax寄存器,则需要P在调用Q前保存。*一般而言P调用Q之后不需要用到eax寄存器,所以一般会将操作数优先存在eax,edx,ecx这类寄存器,以减少准备阶段和结束阶段的开销。 被调用者保存寄存器:ebx,esi,edi如果被调用者Q需要用到ebx,则Q需要保存到栈中再使用。 ebp:帧指针寄存器,指向当前栈帧底部,高地址 esp:栈指针寄存器,指向当前栈帧顶部,低地址。如果以向上为高地址的话,栈就是个倒置的桶,栈帧底部abp描述的就是杯底,底部为高地址,没毛病。 栈和栈帧的变化 相应的汇编指令12345678910int add(int x,int y){ return x+y;}int caller(){ int t1 = 125; int t2 = 80; int sum = add(t1,t2); return sum;} 汇编代码 操作 pushl %ebp 将旧ebp地址压栈(以便回到原栈底),esp地址-4,指向下一个存储单元 movl %esp,%ebp 将esp的值赋给ebp,ebp指向了当前栈帧的底部 subl $24,%esp 把esp减去了24,开辟内存空间 movl $125,-12(%ebp) 125送到ebp-12的位置 movl $80,-8(%ebp) 80这个立即数送到ebp-8的位置 movl -8(%ebp),%eax movl %eax,4(%esp) 把ebp-8的数送到esp+4的位置,在对t2参数进行赋值 movl -12(%ebp),%eax movl %eax,(%esp) 同理,在进行t1参数的赋值 call add 转入add函数体执行,同理第一二条指令是push ebp;mov esp,ebp形成add栈帧的底。return返回参数总是在eax中。call指令总会把下一条指令(movl %eax,-4(%ebp))压入到栈中。add最后一条指令是ret指令,ret指令会将返回地址取出送到eip寄存器(指令指针)中。 movl %eax,-4(%ebp) 将eax的值(即return的和)传送至ebp-4的位置 movl -4(%ebp),%eax 把返回值sum再放入eax寄存器中 leave 退栈,将ebp的内容传送到esp,将esp又指向了ebp;再执行pop指令,把esp指向的内容弹出到ebp内,恢复ebp的内容(ebp回到了p过程的栈底)。esp指向上一个单元,该单元存放的是返回地址。 ret return指令又可以返回到p(调用函数)中执行 准备参数入口,将值再复制了一遍是为什么?假设回调函数为swap(&t1,&t2),要改变局部变量的之前是不是得先存起来,不然令t1=t2,t1原来的值消失了怎么办,简单比喻一下。]]></content>
<tags>
<tag>C</tag>
</tags>
</entry>
<entry>
<title><![CDATA[栈堆]]></title>
<url>%2F2019%2F09%2F16%2F%E6%A0%88%E5%A0%86%2F</url>
<content type="text"><![CDATA[数据结构的栈和堆 栈:只能在表的一端进行插入和删除的线性表。具有先进后出的性质,可以理解为装数据的桶。 堆:经过排序的树形数据结构。 内存分配的栈和堆(以c++编译程序为例)存放区域 区域 作用 栈区(stack) 由编译器自动分配和释放,存放函数的参数值,局部变量的值等。操作方式类似与数据结构中的栈 堆区(heap) 一般由程序员分配和释放,若程序员不释放,程序结束时可能由操作系统回收。与数据结构中的堆是两码事,分配方式类似于链表。 全局区(静态区)(static) 全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的还有一块区域。程序结束后由系统释放 文字常量区 常量字符串,程序结束后由系统释放 程序代码区 存放函数体的二进制代码 没有手动释放堆区的情况1 从C语言本身设计来说,不会释放。所谓动态内存,是由malloc系列函数进行申请的内存,除非在程序中使用free释放,否则该段内存不会被释放掉。从这个角度来说,即使进程结束,该段内存也会被占用。这种现象被称为内存泄露。2 大多数操作系统可以智能释放。动态内存由于是进程使用,向操作系统控制方申请的内存,所以操作系统内核可以记录哪些内存由哪个进程使用,这样为了减少内存泄露的危害,操作系统内核均实现了在进程退出后,进程分配的自用内存自动回收的机制。 来看一个例子1234567891011121314//main.cppint a = 0;//全局初始化区char *p1;//全局未初始化区,p1是以地址为数据么?main(){ int b;//栈 char s[] = "abc";//栈 “abc”也是在栈区么?还是在常量区?与p3的区别?数组在定义初始化的时候就在栈区开辟了一段连续的内存空间 char *p2 ;//栈 char *p3 = "123456";//p3在栈区;"12345\0"在常量区,为什么不在堆区?因为没有申请储存空间,只能存放在常量区 static int c =0; p1 = (char *)malloc(10); p2 = (char *)malloc(20);//分配得来得10和20字节的区域就在堆区。 strcpy(p1,"123456");//123456\0放在常量区。编译器可能会将它与p3所指向的"123456"优化成一个地方 strcpy(p2,"abcdef");//把常量区的"abcdef"拷贝一份到申请的堆区} 申请后系统的响应 栈:仅仅要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。 堆:首先应该知道操作系统有一个记录空暇内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空暇结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样。代码中的delete语句才干正确的释放本内存空间。所以堆是不连续的内存区域。(栈区和全局区的内存分布也不是连续的) 1234567891011121314151617#include <stdio.h>int g1=0, g2=0, g3=0;int main(){ static int s1=0, s2=0, s3=0; int v1=0, v2=0, v3=0; //打印出各个变量的内存地址 printf("0x%08x\n",&v1);//打印各本地变量的内存地址 printf("0x%08x\n",&v2); printf("0x%08x\n\n",&v3); printf("0x%08x\n",&g1); //打印各全局变量的内存地址 printf("0x%08x\n",&g2); printf("0x%08x\n\n",&g3); printf("0x%08x\n",&s1); //打印各静态变量的内存地址 printf("0x%08x\n",&s2); printf("0x%08x\n\n",&s3); return 0;} 另外。因为找到的堆结点的大小不一定正好等于申请的大小。系统会自己主动的将多余的那部分又一次放入空暇链表中。 能够申请的内存大小 栈:在Windows下,栈是向低地址扩展的数据结构。是一块连续的内存的区域栈顶的地址和栈的最大容量是系统预先规定好的。 堆:堆是向高地址扩展的数据结构,是不连续的内存区域。堆的大小受限于计算机系统中有效的虚拟内存。 参考链接]]></content>
<tags>
<tag>C</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Hello World]]></title>
<url>%2F2019%2F08%2F21%2Fhello-world%2F</url>
<content type="text"><![CDATA[Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub. Quick StartCreate a new post1$ hexo new "My New Post" More info: Writing Run server1$ hexo server More info: Server Generate static files1$ hexo generate More info: Generating Deploy to remote sites1$ hexo deploy More info: Deployment]]></content>
</entry>
<entry>
<title><![CDATA[webpack-2]]></title>
<url>%2F2019%2F08%2F21%2Fwebpack-2%2F</url>
<content type="text"><![CDATA[开发环境 mode: 'development', devtool: 'inline-source-map',……plugins: [ 使用 source map ,准确地知道错误来自于哪个源文件, watch mode npm run watch 浏览器可自动更新 webpack-dev-server/webpack-dev-middleware 对server.js文件进行配置,npm start 在3000端口运行此时dist文件夹不会有文件,而是生成在浏览器的缓存中。]]></content>
<categories>
<category>note</category>
</categories>
<tags>
<tag>webpack</tag>
</tags>
</entry>
<entry>
<title><![CDATA[webpack学习笔记]]></title>
<url>%2F2019%2F08%2F08%2Fwebpack%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%2F</url>
<content type="text"><![CDATA[[TOC]看懂webpack.config.js entry 123module.exports={ entry:'./path/to/my/entry/file.js'};默认值是./src/index.js;入口起点 output 12345678const path = require('path');module.exports = { entry: './path/to/my/entry/file.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'my-first-webpack.bundle.js' }}; path定义导入的模块,filename指bunble生成的名件名称及地址。输出文件默认放入./dist文件夹中 loader loader 能够import任何类型的模块,例如css。 123456789101112const path = require('path');module.exports = { output: { filename: 'my-first-webpack.bundle.js' }, module: { rules: [ { test: /\.txt$/, use: 'raw-loader' } ] }}; “嘿,webpack 编译器,当你碰到「在 require()/import 语句中被解析为 ‘.txt’ 的路径」时,在你对它打包之前,先 >使用 raw-loader 转换一下。” plugin 12345678910111213const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装const webpack = require('webpack'); // 用于访问内置插件module.exports = { module: { rules: [ { test: /\.txt$/, use: 'raw-loader' } ] }, plugins: [ new HtmlWebpackPlugin({template: './src/index.html'}) ]}; 该插件为应用程序生成html文件,并注入所有生成的bundle。 资源管理以加载images图像为例 npm install --save-dev file-loader webpack.config 12345678910111213module.exports = { .... module: { rules: [ { test: /\.(png|svg|jpg|gif)$/, use: [ 'file-loader' } ] }} src/icon.jpg src/index.js 1234567891011121314import _ from 'lodash';import './style.css';import Icon from './icon.png';function component() { var element = document.createElement('div'); // lodash,现在由此脚本导入 element.innerHTML = _.join(['Hello', 'webpack'], ' '); element.classList.add('hello'); var myIcon = new Image(); myIcon.src = Icon; element.appendChild(myIcon); src/style.css 123.hello{ background: url('./icon.png');} npm run build 其他加载,css,fonts,data。 管理输出 以下插件/操作均为方便管理dist文件夹,不需要手动更改 动态生成bunble.js文件 1234567891011121314const path = require('path'); module.exports = {- entry: './src/index.js',+ entry: {+ app: './src/index.js',+ print: './src/print.js'+ }, output: {- filename: 'bundle.js',+ filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') } }; npm install --save-dev html-webpack-plugin 123456789101112131415161718 const path = require('path');+ const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: { app: './src/index.js', print: './src/print.js' },+ plugins: [+ new HtmlWebpackPlugin({+ title: '管理输出'+ })+ ], output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') } }; 执行npm run build该插件会默认生成index.html文件,覆盖/更改原index.html文件 npm install --save-dev clean-webpack-plugin 123456+ const CleanWebpackPlugin = require('clean-webpack-plugin');//需改为{CleanWebpackPlugin},否则会有报错.... plugins: [+ new CleanWebpackPlugin(), 作用为清理dist文件夹中之前生成的不需要的旧文件。这一节管理输出一直有报错,未能正常显示页面 1234567891011121314Uncaught ReferenceError: ducument is not defined at app.bundle.js:9 at Module.<anonymous> (app.bundle.js:9) at r (app.bundle.js:1) at app.bundle.js:1 at app.bundle.js:1(anonymous) @ app.bundle.js:9(anonymous) @ app.bundle.js:9r @ app.bundle.js:1(anonymous) @ app.bundle.js:1(anonymous) @ app.bundle.js:1//浏览器报错Entrypoint undefined = index.html//终端报错,有人说是插件本身的问题 按照官网操作来的,这报错真的不明白,之后再看看怎么解决唔,看看github相关插件的issue 吐血!document拼写错误,见devtool: 'inline-source-map',可跟踪错误至源文件]]></content>
<categories>
<category>note</category>
</categories>
<tags>
<tag>webpack</tag>
</tags>
</entry>
</search>