SegmentFault 我不懂前端最新的文章
2018-12-21T18:53:12+08:00
https://segmentfault.com/feeds/blogs
https://creativecommons.org/licenses/by-nc-nd/4.0/
一道面试题引发的思考 --- Event Loop
https://segmentfault.com/a/1190000017476256
2018-12-21T18:53:12+08:00
2018-12-21T18:53:12+08:00
ShiRouMi
https://segmentfault.com/u/shiroumi
1
<p>想必面试题刷的多的同学对下面这道题目不陌生,能够立即回答出<strong>输出10个10</strong>,可是你真的懂为什么吗?为什么是输出10?为什么是输出10个10?这两个问题在我脑边萦绕。嗯,我得说服自己。</p>
<pre><code class="js">for (var i = 0; i < 10; i++) {
setTimeout(() => {
console.log(i)
}, 0)
}</code></pre>
<p>JavaScript 是单线程。(ok,我又问自己它为什么是单线程 ==》作为浏览器语言,JS的用途是与用户交互以及操作DOM,如果是多线程会引发很多问题,浏览器无法判断以哪个线程为标准,因此它只能是单线程)<br>任务分为「同步任务」与「异步任务」。同步任务都好理解,一个执行完执行下一个。异步任务稍许复杂。<br>异步任务运行机制:</p>
<ul>
<li>同步任务在「主线程」上执行,形成一个「执行栈」(execution context stack)</li>
<li>主线程之外,还有一个「任务队列」(task queue),异步任务有了运行结果,就在任务队列里放置一个事件</li>
<li>执行栈中同步任务执行完,就会去读取任务队列的事件,异步任务事件结束等待状态,进入执行栈执行</li>
<li>主线程重复前面三步</li>
</ul>
<p>其实屡清楚很好理解,以上运行机制又称为 <code>Event Loop</code>(事件循环)<br>我们回到这道面试题, 一起来理解下:</p>
<pre><code>for 循环是同步任务,setTimeout 是异步任务
首先 for 循环在主线程上执行,setTimeout 进入任务队列
同步任务执行完,i = 10,此时 setTimeout 被唤醒进入执行栈执行
因此输出的值为10
可是为什么会输出10个10呢?到现在我还是没有完全说服自己。</code></pre>
<p>我们对代码稍作修改,</p>
<pre><code class="js">for (var i = 0; i < 10; i++) {
console.log(i)
var p = setTimeout(() => {
console.log(i)
}, 0)
console.log(p)
}</code></pre>
<p>输出结果</p>
<pre><code class="js">0
Timeout {_called: false, _idleTimeout: 1, _idlePrev: TimersList, _idleNext: TimersList, _idleStart: 1893, …}
1
Timeout {_called: false, _idleTimeout: 1, _idlePrev: TimersList, _idleNext: Timeout, _idleStart: 3739, …}
2
Timeout {_called: false, _idleTimeout: 1, _idlePrev: TimersList, _idleNext: Timeout, _idleStart: 4924, …}
......
9
Timeout {_called: false, _idleTimeout: 1, _idlePrev: TimersList, _idleNext: Timeout, _idleStart: 11733, …}
10
10
...
10</code></pre>
<p>看到这个结果大家是否有些清楚了呢,我们重新梳理下原先的面试题</p>
<pre><code>首先 for 循环在主线程上执行,setTimeout 内部的回调函数进入任务队列
for 循环里,i 每次执行一次,异步队列里放置一个 setTimeout 回调
同步任务执行完,i = 10, 此时异步队列里放置了10个回调事件
setTimeout 被唤醒进入执行栈执行
因此输出了10个10</code></pre>
<p>当然了, <code>Event Loop</code> 的知识不止这点,涉及到的东西也很多。本文只是我对这道面试题的一点思考,有误的地方望批评指正。</p>
<p>以下几篇是我收藏的好文,供大家学习参考~<br><a href="https://link.segmentfault.com/?enc=BBLyXFWodxy%2BSE2%2FwkmZ5w%3D%3D.bao3RU4m4N6a1jogR0Qi%2B%2FFQGnaL4M7AV7iEaqFHmBTcQPML1B9L5Wd6woI7OzCpwXevRS%2B44PMKTx21O5C9YQ%3D%3D" rel="nofollow">JavaScript 运行机制详解:再谈Event Loop</a><br><a href="https://link.segmentfault.com/?enc=xlj6drbtTe4LsCoheDBLYA%3D%3D.dCEEImXpxu5O%2ByUsh%2F%2FkI9z6Hq%2BCvXv1e8CTGdGmiFB%2Bor3rUuCh5hlnv8IqTWSv" rel="nofollow">这一次,彻底弄懂 JavaScript 执行机制</a><br><a href="https://link.segmentfault.com/?enc=PZYsviTuAI74M0H4mZWVMw%3D%3D.9geE%2FAleWPKMazpOvsCabgKumShZ9QvJoJeWVQzEY273zNpqwFoOmvVtH7Z4jnWs" rel="nofollow">从event loop规范探究javaScript异步及浏览器更新渲染时机</a></p>
一道面试题引发的思考 --- 理解 new 运算符
https://segmentfault.com/a/1190000017460276
2018-12-20T16:19:57+08:00
2018-12-20T16:19:57+08:00
ShiRouMi
https://segmentfault.com/u/shiroumi
0
<p>今天看到一道面试题,如下,问: 实例化 <code>Person</code> 过程中,<code>Person</code>返回什么(或者 <code>p</code> 等于什么)?</p>
<pre><code class="js">function Person(name) {
this.name = name
return name;
}
let p = new Person('Tom');</code></pre>
<p>说实话,第一反应我以为值为 <code>'Tom'</code>,等到我把代码丢到控制台一输出,才明白我错了。天呐,<code>new</code>运算符给无视掉了吗???<br>撇开 <code>new</code> 的存在,我们修改下代码</p>
<pre><code class="js">function Person(name) {
this.name = name
return name;
}
let p = Person('Tom');
console.log(p);</code></pre>
<p>很显然,输出的结果是 <code>'Tom'</code>, 但是有 <code>new</code> 存在呢?接下去,我们来捋一捋。<br>首先,我先去 <a href="https://link.segmentfault.com/?enc=cxbj5iM4NQ1AzK6VRlJrDA%3D%3D.biy%2FNhozO9KlijH5F8j%2FjXhlTdjQBugeApbITS7Zyv5S4QXkMiiGJmk8NS0UjY53wbv%2FG1mp77scV1ul4OQRui6FgEK%2Be6yP7wgwFItyVaw%3D" rel="nofollow">MDN</a>上搜索了 new 的定义</p>
<blockquote>new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。</blockquote>
<p>emmmm,相当晦涩难懂。</p>
<p>那我们试着写几个栗子看看结果吧</p>
<pre><code class="js">function Person1(name) {
this.name = name;
// 没有返回值
}
function Person2(name) {
this.name = name;
return name;
// 返回非对象
}
function Person3(name) {
this.name = name;
return { a: 1 };
// 返回对象
}
function Person4(name) {
this.name = name;
return null;
// 返回null
}
var p1 = new Person1("aa");
var p2 = new Person2("bb");
var p3 = new Person3("cc");
var p4 = new Person4("dd");
console.log(p1); // Person1 {name: "aa"}
console.log(p2); // Person2 {name: "bb"}
console.log(p3); // {a: 1}
console.log(p4); // Person4 {name: "dd"}</code></pre>
<p>根据上面几个栗子,我们能得出结论:当使用 new 来创建对象||调用构造函数时,如果函数没有返回值|| 返回值是非对象,那么返回的就是构造函数实例后的对象;如果函数return对象,那么返回这个对象(特例:<code>return null</code>,返回的也是构造函数实例后的对象而非<code>null</code>)</p>
<p>我们接着看 <a href="https://link.segmentfault.com/?enc=rw2IDbqPbttl%2Fhbqb3ZObw%3D%3D.t5OiQYcD9EP4VN8xdWbTkaPwD0c%2FEi5XrThtveM4CMhOmKVS%2BsTmJkC7t8lIigZUekJnDqgGmXoATb4b99gxi36ur09PjdEveDXXPVOOhLo%3D" rel="nofollow">MDN</a> 文档的解释,毕竟光光看这几个demo没有说服力。</p>
<p><img src="/img/bVblqcQ?w=1418&h=460" alt="clipboard.png" title="clipboard.png"></p>
<p>一起来理解下 <code>new</code> 到底做了什么工作吧~<br>就拿下面这个 <code>demo</code>分析</p>
<pre><code class="js">function Person(name) {
this.name = name;
return {a: 1}
}
var p = new Person('fefeng')</code></pre>
<p>当调用 <code>new Person(...)</code>时,会进行以下几步:</p>
<ul>
<li>首先是 继承自 <code>Person.prototype</code>的新对象会被创建</li>
<li>使用参数 <code>'fefeng'</code> 调用构造函数 <code>Person</code>, 并将 <code>this</code> 绑定到新创建的对象</li>
<li>由 <code>Person</code> 返回的对象就是 <code>new</code> 表达式的结果 =》 <code>Person</code> 返回的对象是 <code>{a: 1}</code> 所以<code>new</code> 表达式的结果为 <code>{a:1}</code> ; 如果 <code>Person</code> 没有返回值(一般构造函数都不返回值)那么使用步骤1创建的对象,即==》 继承自 <code>Person.prototype</code> 的新对象</li>
</ul>
<p>貌似照着文档能够些许理解了,倘若模拟实现 <code>new</code>运算符更能深入理解 <code>new</code><br>以下是 new 的模拟实现,代码来源 : <a href="https://link.segmentfault.com/?enc=fqVIScf%2BYh4DWYJm6ahTug%3D%3D.ApwAHYjDqos8Py0CZvdPsRDMBfeB6tj18MR2OXSFfX9hDolGtHPpE%2BScLNhYyi86" rel="nofollow">JavaScript深入之new的模拟实现</a></p>
<pre><code class="js">function objectFactory() {
var obj = new Object(),
cons = [].shift.call(arguments)
obj.__proto__ = cons.prototype
var ret = cons.apply(obj, arguments)
return typeof ret === 'object' ? ret|| obj : obj
}
function Person(name) {
this.name = name;
return {a: 1}
}
var p = objectFactory(Person, 'fefeng')</code></pre>
<p>当然了,学习别人的代码不能仅仅只是照搬过来,起码得理解这个代码吧。<br>使用</p>
<ul>
<li>首先是创建一个对象,</li>
<li>
<code>cons</code> 是调用 <code>objectFactory</code> 方法的第一个参数,即构造函数; 因为 <code>shift</code> 会改变原数组,所以改变后的 <code>argument</code> 即为调用构造函数的参数 (这里补充说明下: arguments 是一个对应于传递给函数的参数的类数组对象。)</li>
<li>将 <code>obj</code> 的原型指向构造函数, 这样 obj 就能访问到构造函数原型上的属性</li>
<li>将构造函数 <code>cons</code> 的 <code>this</code> 指向 <code>obj</code>,这样 <code>obj</code> 能访问构造函数里的属性</li>
<li>判断返回的值是不是一个对象,如果是对象即返回它(当然这里要处理 <code>return null</code> 的特例);如果不是对象就返回 <code>obj</code> (注意:这里的 <code>obj</code> 已经不是一个空对象)</li>
</ul>
<p>如果你耐心看到了这里,那么十分感谢。如文章有错误,望给予指正~</p>
各类型的 toString 方法合集
https://segmentfault.com/a/1190000017337157
2018-12-11T16:21:42+08:00
2018-12-11T16:21:42+08:00
ShiRouMi
https://segmentfault.com/u/shiroumi
2
<h2>toString()</h2>
<pre><code class="js">Object.prototype.toString()
Array.prototype.toString()
Boolean.prototype.toString()
RegExp.prototype.toString()
String.prototype.toString()
Number.prototype.toString()
Function.prototype.toString()
Date.prototype.toString()</code></pre>
<p><code>Array,Boolean,RegExp,String,Number,Function,Date</code>这些都是覆盖了 <code>Object</code> 的 <code>toString</code> 方法</p>
<h3>Object.prototype.toString()</h3>
<p>返回一个表示该对象的字符串</p>
<blockquote>来自MDN的描述: 每个对象都有一个toString()方法,当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用。默认情况下,toString()方法被每个Object对象继承。如果此方法在自定义对象中未被覆盖,toString() 返回 "[object type]",其中type是对象的类型</blockquote>
<pre><code class="js">var obj = new Object()
obj.toString() // "[object Object]"</code></pre>
<h4>可以覆盖默认的 toString 方法</h4>
<p><code>toString()</code> 方法不能传入参数, 必须要返回一个字符串</p>
<pre><code class="js">function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.toString = function() {
return 'my name is ' + this.name + ', my age is ' + this.age
}
var fe = new Person('fe_feng', 23)
fe.toString() // "my name is fe_feng, my age is 23"</code></pre>
<h4>使用 toString() 检测对象类型</h4>
<pre><code class="js">var toString = Object.prototype.toString;
toString.call(new Date); // [object Date]
toString.call(new String); // [object String]
toString.call(Math); // [object Math]
//Since JavaScript 1.8.5
toString.call(undefined); // [object Undefined]
toString.call(null); // [object Null]</code></pre>
<h3>Boolean.prototype.toString()</h3>
<p>返回指定的布尔对象的字符串形式<br>返回值为 <code>"true"</code> 或者 <code>"false"</code><br><code>Boolean</code> 对象覆盖了 <code>Object</code> 对象的 <code>toString</code> 方法<br>没有继承 <code>Object.prototype.toString()</code><br>当一个 <code>Boolean</code> 对象作为文本值或进行字符串连接时, JS会自动调用其 <code>toString</code> 方法</p>
<pre><code class="js">'1' + true // "1true"</code></pre>
<h3>Array.prototype.toString()</h3>
<p>返回一个表示指定的数组及其元素的字符串。<br><code>Array</code> 对象覆盖了 <code>Object</code> 的 <code>toString</code> 方法<br><code>toString</code> 方法连接数组并返回一个字符串,其中包含用逗号分隔的每个数组元素</p>
<p>运用这个方法,可以处理<strong>数组扁平化</strong>, 不过有一些限制。</p>
<ul><li>当数组元素都是字符串时,扁平化处理很简单</li></ul>
<pre><code>function flatten(arr) {
return arr.toString().split(',')
}</code></pre>
<ul><li>当数组元素都是数字时,扁平化处理如下</li></ul>
<pre><code class="js">function flatten(arr) {
return arr.toString().split(',').map(item => +item)
}</code></pre>
<ul><li>当数组元素不确定时候,这个方法就不行了(返回的数组会改变原数组对象)</li></ul>
<h3>Number.prototype.toString()</h3>
<p>返回指定 Number 对象的字符串表示形式</p>
<pre><code class="js">numObj.toString([radix])
// radix: 指定要用于数字到字符串的转换的基数(从2到36)。如果未指定 radix 参数,则默认值为 10。
// 如果 toString() 的 radix 参数不在 2 到 36 之间,将会抛出一个 RangeError。</code></pre>
<blockquote>栗子来源 MDN</blockquote>
<pre><code class="js">var count = 10;
console.log(count.toString()); // 输出 '10'
console.log((17).toString()); // 输出 '17'
console.log((17.2).toString()); // 输出 '17.2'
var x = 6;
console.log(x.toString(2)); // 输出 '110'
console.log((254).toString(16)); // 输出 'fe'
console.log((-10).toString(2)); // 输出 '-1010'
console.log((-0xff).toString(2)); // 输出 '-11111111'</code></pre>
迭代是什么?
https://segmentfault.com/a/1190000017113077
2018-11-23T10:31:40+08:00
2018-11-23T10:31:40+08:00
ShiRouMi
https://segmentfault.com/u/shiroumi
1
<p>经常看到<code>迭代</code>这个词,那么归总下吧~</p>
<blockquote>什么是<strong>可迭代对象</strong>:</blockquote>
<p>一个对象(或它的原型链上的某个对象)必须有一个名字是 <code>Symbol.iterator</code>的属性</p>
<ul>
<li>
<code>String Array Map Set</code> 是内置可迭代的对象,因为它们的原型对象都有一个 <code>@@iterator</code> 方法</li>
<li>
<code>Object</code> 不是</li>
</ul>
<blockquote>接收可迭代对象的API</blockquote>
<p><strong>数组的遍历</strong> 会调用迭代器接口,所以使用到数组作为参数的场合,都使用了遍历器</p>
<ul>
<li>
<code>Map([iterable])</code> <code>WeakMap([iterable])</code>
</li>
<li>
<code>Set([iterable])</code> <code>WeakSet([iterable])</code>
</li>
<li>
<code>Promise.all([iterable])</code> <code>Promise.race([iterable])</code>
</li>
<li><code>Array.from([iterable]) </code></li>
<li>
<code>for...in</code> <code>for...of</code>
</li>
<li><code>getOwnPropertyNames </code></li>
</ul>
<p><strong>不能用 <code>for...in</code> 来迭代 Set, 因为 Set 中的元素没有 key</strong>, 使用 <strong>for...of</strong> 遍历</p>
<blockquote>迭代器 与 迭代对象的差别:</blockquote>
<p><code>迭代器</code>是一种特殊的对象,每一个迭代器都有一个 <code>next()</code>,该方法返回一个对象,包括 <code>value</code> 和<code>done</code> 属性</p>
<pre><code class="js"> // 实现一个返回迭代器对象的函数
function createIterator(items){
var i = 0;
return {
next () {
var done = (i >= items.length);
var value = !done ? items[i++] : 'undefined'
return {
done,
value
}
}
}
}
const a = createIterator([1, 2, 3]);
//该方法返回的最终是一个对象,包含value、done属性。
console.log(a.next()); //{value: 1, done: false}
console.log(a.next()); //{value: 2, done: false}
console.log(a.next()); //{value: 3, done: false}
console.log(a.next()); //{value: undefined, done: true}</code></pre>
<blockquote>可迭代 和 可枚举的差别</blockquote>
<blockquote>举例说明<strong>不可迭代</strong>
</blockquote>
<pre><code class="js">let arrayLike = {
0: 'aa',
1: 'bb',
2: 'cc',
length: 3
}
for (var item of arrayLike) {
console.log(item) // TypeError: arrayLike is not iterable
}
let obj = {
a: 'monday',
b: 'sunday'
}
for (var item of obj) {
console.log(item) // TypeError: obj is not iterable
}</code></pre>
<p>上面这个例子是想遍历一个 <code>类数组对象</code> 和 一个 <code>对象</code>, 因为它不可迭代,所以用 <code>for...of</code>不能遍历<br>报错<code>TypeError: arrayLike is not iterable</code></p>
<p>但是使用 <code>for...in</code> 又可以遍历了</p>
<pre><code class="js">for (var item in arrayLike) {
console.log(item) // 0 1 2 length
}
for (var item in obj) {
console.log(item) // a b
}</code></pre>
<p>这是为什么呢?</p>
<p>附上阮一峰老师的解释</p>
<pre><code>对象(Object)之所以没有默认部署Iterator接口,是因为对象的哪个属性先遍历,哪个属性后遍历是不确定的,需要开发者手动指定。本质上,遍历器是一种线性处理,对于任何非线性的数据结构,部署遍历器接口,就等于部署一种线性转换。不过,严格地说,对象部署遍历器接口并不是很必要,因为这时对象实际上被当作Map结构使用,ES5没有Map结构,而ES6原生提供了。</code></pre>
<p>以及知乎上的解答<a href="https://link.segmentfault.com/?enc=Kj1xcZhP9R1mahi%2FElKKGA%3D%3D.ibSdKAiyEy6W%2BzP41OnJ%2FEmb3%2B8BiN761UFnNI4VVZCfR%2FcahnHJ7QZAWfJUVxmY" rel="nofollow">为什么for...of对象不可迭代---贺师俊回调</a> , 写的很棒,一下就清晰了~</p>
<pre><code class="js">// 几种迭代方式
for (const k of Object.keys(obj)) ... // enumerable own keys
for (const [k, v] of Object.entries(obj)) ... // enumerable own [key, value]s
for (const k of Object.getOwnPropertyNames(obj)) // all own keys
for (const s of Object.getOwnPropertySymbols(obj)) // all own symbols
for (const k of Reflect.ownKeys(obj)) // all own keys (include symbols)
</code></pre>
关于箭头函数的思考
https://segmentfault.com/a/1190000017104335
2018-11-22T16:36:50+08:00
2018-11-22T16:36:50+08:00
ShiRouMi
https://segmentfault.com/u/shiroumi
0
<h2>关于箭头函数的思考</h2>
<p>自从有了ES6箭头函数这个灵活的功能,现在写函数都习惯写成箭头函数格式</p>
<pre><code class="js">() => {}</code></pre>
<p>然而如果不理解箭头函数的原理,滥用它也会造成很大的影响。<br>思考下面这个问题</p>
<pre><code class="js">let func = value => console.log(value)
<!-- 区别 -->
let func = function(value) {
console.log(value)
}</code></pre>
<p>我们到平台上测试一下, 显然两者不等。</p>
<pre><code class="js">let func = value => console.log(value)
// 等同于
"use strict";
var func = function func(value) {
return console.log(value);
};</code></pre>
<p>而 <code>console.log()</code>方法返回 <code>undefined</code><br><code>return undefined</code> 与没有返回值一样。所以效果上问题中两种写法没有差异。</p>
<pre><code class="js">let func = value => value
// 等同于
let func = value => {
return value
}
// 等同于
var func = function func(value) {
return value;
};</code></pre>
<p><strong>如果箭头函数的代码块部分<code>多于一条语句</code>,就要使用大括号将它们括起来,并且使用return语句返回。</strong></p>
<pre><code class="js">var sum = (num1, num2) => {
console.log('多条语句情况');
return num1 + num2;
}</code></pre>
<p>那如果我使用了<code>箭头</code>,使用了<code>大括号</code>,没使用<code>return</code>呢?</p>
<pre><code class="js">var sum = (num1, num2) => {
console.log('多条语句情况');
num1+num2
}
<!-- 等于-->
'use strict';
var sum = function sum(num1, num2) {
console.log('多条语句情况');
num1 + num2; // 这条语句没有任何作用
};
</code></pre>
<p><strong>由于大括号被解释为代码块,所以如果箭头函数直接返回一个<code>对象</code>,必须在对象外面加上括号</strong></p>
<pre><code class="js">let getInfo = name => ({ name: name, job: "moving bricks" });</code></pre>
<p>使用箭头函数必须要有返回值吗?</p>
<pre><code class="js">// 如果箭头函数只有一行语句,且不需要返回值
let fn = () => void doesNotReturn(); // void</code></pre>
<p>箭头函数表达式:<strong>没有自己的 this arguments super new.target, 不能用作构造函数,没有prototype属性,不能用作生成器</strong></p>
<p>箭头函数不会创建自己的 this, 它只会<strong>从自己作用域链的上一层继承this</strong> 这点很重要。如下面示例,箭头函数内的this的作用域上一层即 <code>Person</code> 函数内的 <code>this</code></p>
<pre><code class="js">function Person(){
this.age = 0;
setInterval(() => {
this.age++; // |this| 正确地指向person 对象
}, 1000);
}
var p = new Person();</code></pre>
<p>箭头函数没有自己的 <code>this</code> 指针, <code>call() apply() bind()</code> 调用的时候, <strong>第一个参数会被忽略</strong> (--- 对于这个,我有疑惑。第一个参数会被忽略是指原先调用 <code>call</code> 方法的 <code>fun.call(thisArg, arg1, arg2, ...)</code>的 <code>thisArg</code> 会被忽略呢?还是 <code>arg1</code>会被忽略呢? 我们来看栗子</p>
<pre><code class="js">var adder = {
base : 1,
add : function(a) {
var f = v => v + this.base;
return f(a);
},
addThruCall: function(a) {
var f = v => v + this.base;
var b = {
base : 2
};
return f.call(b, a);
},
addTest: function(a) {
var f = v => v + this.base;
var b = {
base : 2
};
return f.call(b, 23, a);
}
};
console.log(adder.add(1)); // 输出 2
console.log(adder.addThruCall(1)); // 仍然输出 2
console.log(adder.addThruCall('a')); // 输出 24 (23+1)</code></pre>
<p>很明显了,<strong>第一个参数会被忽略</strong> 是忽略 <code>call</code> 方法里的第一个参数(好像是我在思考的时候想多了,这很明显呀)</p>
<p>参考资源</p>
<ul>
<li><a href="https://link.segmentfault.com/?enc=wU5kDtw8mrFUyaaM%2BKRqyQ%3D%3D.Z0Yks91eu1ewPXWbLd1KQ5kfHJIH5%2BJYAwk0Pef5e7nEvw5pWxnQKsRYxkPuV%2Fk%2F5027RNvqILZZW0uQ4zhUaewqDAr8Rda3EIJPwF239HQyk49PUP1Ln9I1J2lvSgRusobeMrsN99RqPXRu4Fy9OwYfSIjFln2WzaToBE7bPemdPcaqWHpnHWNhdd0hpG0V" rel="nofollow">es6 箭头函数 -- 阮一峰</a></li>
<li><a href="https://link.segmentfault.com/?enc=kae0Lv7%2F%2BuHckLU8qYvcQA%3D%3D.4Sk7GPIX09V%2F6pMm7aatqC0Bv%2BRlLpPbwDQEQEcXvrZ6ryfuAe01OG1xNuyq1ZbCEryMu7N1GSOanXc%2FlP21TFVAISuLmO0wlG8F6kIVuxhvSC9DOZhX8HtvzpzmhoWo" rel="nofollow">箭头函数 MDN</a></li>
</ul>
业务代码如何判断生产/开发环境
https://segmentfault.com/a/1190000016194069
2018-08-29T14:46:08+08:00
2018-08-29T14:46:08+08:00
ShiRouMi
https://segmentfault.com/u/shiroumi
1
<h2>开发环境和生产环境分离的原因</h2>
<p>在开发时,会产生 debug 或者是测试的代码,这些代码不应该出现在生产环境中。当项目部署时,往往会将代码进行一系列的压缩来优化它,如此会彻底破坏代码的可读性。本地开发往往使用的是本地 mock 的数据,而正是上线后用的是真实数据。以上几点,开发环境和生产环境必须得分离。</p>
<p>比如我想让项目根据不同的环境登录不同的地址。本地开发登录的是localhost,测试环境登录的是test地址,上线环境登录的是online地址。那么业务代码里如何判断生产开发测试环境呢?</p>
<h2>方案一 将环境变量配置到 <code>package.json</code>
</h2>
<pre><code class="js">// package.json
{
// npm install cross-env
// 因为mac和windows设置命令环境变量的命令不一致, 所以用cross-env来做兼容
"script": {
"dev": "cross-env NODE_ENV=dev webpack --progress --colors",
"production": "cross-env NODE_ENV=production webpack --progress --colors"
}
}
// npm run dev // 开发
// npm run production // 生产</code></pre>
<p>那么 <code>package.json</code> 的 <code>script</code>中的参数, <code>webpack</code>文件中如何读取呢?<br>在 webpack 中, 可通过 <code>process.env.NODE_ENV</code>获取到变量</p>
<pre><code class="js">// webpack 下
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV)
}
})
]</code></pre>
<p>为什么需要加上<code>JSON.stringify</code>, 因为webpack 打包的时候对变量做替换会将 <code>process.env.NODE_ENV</code> 替换成 <code>production</code>, 而我们期望的是 <code>'production'</code></p>
<p>有个问题,<code>new webpack.DefinePlugin</code> 定义的全局变量是 <code>在编译时可以配置的全局常量。</code>,那么,如何在 <code>webpack config</code> 文件中使用 <code>process.env.NODE_ENV </code>呢?<br>就是上面的回答,在脚本中设置变量 并且用上<code>cross-env</code>。 因此经常两者结合使用。</p>
<h2>方案二:</h2>
<p>使用<code>process.env.npm_lifecycle_event</code> 这个参数来辨别开发环境。<br>npm 提供一个 <code>npm_lifecycle_event</code> 变量,返回当前正在运行的脚本名称<br>这个方法极为简单。</p>
<p><a href="https://link.segmentfault.com/?enc=OtzGXoV0E9zENj2D5mzV0g%3D%3D.ELTq3u1E7vfSgm3boI4OJQLL%2BKppph0qnLEgOM0SXcqIYYbjx1XH4Nht9xv21to5rqbSOgLXuv2gSdO0dqwiAg%3D%3D" rel="nofollow">npm scripts 使用指南</a></p>
<p><a href="https://link.segmentfault.com/?enc=krKDat8KqgMnvBHj%2FxZjsA%3D%3D.HiWlUBuISmtL8LxDssVCE39cS%2BoPFSbe61FREAUTNggCt7Q7YzfdqsgbvV4ulG0L" rel="nofollow">webpack.DefinePlugin使用介绍</a></p>
浏览器原生支持ECMAScript模块,本地测试报CORS policy跨域错误
https://segmentfault.com/a/1190000016113529
2018-08-22T21:22:37+08:00
2018-08-22T21:22:37+08:00
ShiRouMi
https://segmentfault.com/u/shiroumi
1
<p>浏览器已经原生支持ES模块,我阅读着底部参考链接的两篇文章,本地测试</p>
<p><img src="/img/bVbfL1o?w=1420&h=294" alt="clipboard.png" title="clipboard.png"></p>
<p><img src="/img/bVbfL1r?w=1416&h=346" alt="clipboard.png" title="clipboard.png"></p>
<p>打开 <code>html</code> 文件,没有显示预期的内容,打开控制台,报错如下:</p>
<p><img src="/img/bVbfL1A?w=1114&h=210" alt="clipboard.png" title="clipboard.png"></p>
<p>这是为什么?</p>
<hr>
<p>不像常规的脚本加载,直接跨域可以解析,<code>module</code>模式下脚本资源加载需要 CORS 方式获取,不然浏览器报<code>CORS policy</code>跨域相关错误</p>
<p>如何支持跨域?<br>需要模块资源服务端配置 <code>Access-Control-Allow-Origin</code>,可以指定具体域名,<br>或者直接使用* 通配符,<code>Access-Control-Allow-Origin:*</code></p>
<p><strong>意味着代码本地跑不了,至少得放服务器上,而且服务器需要配置配置<code>Access-Control-Allow-Origin</code></strong></p>
<p>于是我将代码放到的 github上,正常显示预计效果,打开控制台也没有报错 <a href="https://link.segmentfault.com/?enc=k1vN7JUQlbfOiNxGD3DuFQ%3D%3D.oGqp96OOK3FjL4AOw71ww4JpuSF0jIO%2Fg0sHuXRSV0EDlFvGyan2OxjiV6DjHaZeBhX4AA6W5heAix9lYt9J61dYeNnClcmAz8YLCA6T7q5VDX88HvHbGgujLtjNuE9M1odvO30Or2Slr%2BrGCwYYZGT91OjtGuVZD4WaTrzeNFY%3D" rel="nofollow">点击查看测试</a></p>
<p>学习详细内容请参考下面的两篇文章~</p>
<p>参考链接:<br><a href="https://link.segmentfault.com/?enc=bV5lWO3OlnrOJaJlaUbYJg%3D%3D.4GIHM5N6EUDkwtSRDX0mrJnq%2FEpzTMAWdUriNxqct6NNX9%2Bfq5z6o2opfzzzVMebooH9lgO0qPyzGyx%2FyrBXNg%3D%3D" rel="nofollow">ECMAScript modules in browsers</a><br><a href="https://link.segmentfault.com/?enc=MTDWe5LTTTr0bwAw%2FKLFaw%3D%3D.Zjw7f9coTDwUqisOpeFAnGYfYQdvZzkhkF6Gadjb6Q%2BZ1D2StziNHvQwv2lWU4jmqGPTyxHoQx%2BfqGiODw4mskqAAX1yGlRQnIt8qaYmFBOmFmj%2FMW%2FPhGfG3i1I7TK6" rel="nofollow">万岁,浏览器原生支持ES6 export和import模块啦!</a></p>
属性的可枚举性与不可枚举性
https://segmentfault.com/a/1190000014745723
2018-05-06T14:46:57+08:00
2018-05-06T14:46:57+08:00
ShiRouMi
https://segmentfault.com/u/shiroumi
11
<p>我们到MDN上搜索<a href="https://link.segmentfault.com/?enc=7uja1PqxYTOCmaJHLPb%2FQA%3D%3D.eD3Ymy1mTBnkB%2FXOvndiN9xR1GosL%2FAas%2Ft%2BTXY486vVEE9YT%2BPIeT9qOzMwH7k9%2FvZ9XnBzRzIr%2BfGwhh9GwBNKyl8rNSJCS7F3OZRzECedIeD8rO0PzcO3Qy%2FoNregJJJV3tuxZlLKqeDeK0RF4w%3D%3D" rel="nofollow">属性的可枚举性和所有权</a></p>
<p>可枚举属性是指内部可枚举标志(<code>enumerable</code>)设置为<code>true</code>的属性,自然不可枚举属性即是<code>enumerable</code>为<code>false</code></p>
<p>我们看下JavaScript基本类型和基本类型包裹对象</p>
<p><code>基本类型</code>是指非对象且无方法的数据。<br><code>JavaScript</code>有6种基本类型:<code>string,number,boolean,null,undefined,symbol</code><br>除了<code>null和undefined</code>外,所有基本类型值都有包裹这个<code>基本类型值的等价对象</code>:<br><code>String</code> <code>Boolean</code> <code>Number</code> <code>Symbol</code><br><code>JavaScript</code>有7种不同类型的值</p>
<ul>
<li>
<p>六种 <code>原型</code> 数据类型</p>
<ul>
<li>Boolean</li>
<li>String</li>
<li>Number</li>
<li>null</li>
<li>undefined</li>
<li>Symbol</li>
</ul>
</li>
<li>
<code>Object</code>对象</li>
</ul>
<p>还有一个概念就是<code>字面量</code>,有以下字面量</p>
<ul>
<li>数组字面量(Array)</li>
<li>布尔字面量 (Boolean)</li>
<li>浮点数字面量 (Float)</li>
<li>整数 (Int)</li>
<li>对象字面量 (Object)</li>
<li>RegExp</li>
<li>字符串字面量 (String)</li>
</ul>
<p>好了好了扯远了,为什么说这些呢,抛出问题,以上属性是否可枚举呢?</p>
<p>下面来看一个例子:</p>
<pre><code>function Person() {
this.name = 'fyflying'
}
Person.prototype = {
hobby: 'coding'
}
var person = new Person()
Object.defineProperty(person, 'sex', {
value: 'female'
})
for (var item in person) {
console.log(item + ':' + person[item])
}
/**
name:fyflying
hobby:coding
**/
Object.keys(person)
// ["name"]
JSON.stringify(person)
//"{"name":"fyflying"}"</code></pre>
<p>可以看到除了<code>sex</code>属性其他都遍历到了,而且使用<code>for..in</code>与<code>Object.keys</code>还是有区别的,区别就在于使用<code>for...in</code>还会遍历出<strong>对象从原型链上继承来的可枚举属性</strong></p>
<p>通过<code>Object.defineProperty</code>定义的属性,该标志值默认是<code>false</code>, 所以不可枚举,通过<code>for..in</code> 和 <code>Object.keys()</code>都遍历不到。</p>
<p><strong>判断可枚举属性与不可枚举属性方法</strong>:<br><code>propertyIsEnumerable()</code>: 方法返回一个布尔值,表示指定的<strong>自身属性</strong>是否可枚举。(不包括原型链继承的属性)</p>
<blockquote>区别以下方法:</blockquote>
<p>使用 <code>for...in</code>迭代,遍历出<strong>自身以及原型链上的可枚举的属性</strong>,通过<code>hasOwnProperty</code>进行筛选能遍历出<strong>自身可枚举属性</strong><br>而使用<code>Object.keys</code>直接遍历出的<strong>自身可枚举属性组成的数组</strong><br>使用<code>getOwnPropertyNames</code>可以访问<strong>自身可枚举属性与不可枚举属性</strong></p>
<p>另外,还有大漠的文章参考,对于以上几个方法介绍的很详细。<a href="https://link.segmentfault.com/?enc=xKr6IEe1VUdMXHD2I%2B9ODA%3D%3D.Zq1dTJzsyfcmeFx0sVkLgn8JFNQQO0gcDYM9n1rrmwzRTnLKMRneU0wd6Chw9OsOKWb4UyRWUd9TwTbhpw6DBPQgtYhDf4AgLubCYKegt7LOyU%2BPnyhjWoSbU2HrF3dL7vTTrzSDIwam6tTdxLYnTA%3D%3D" rel="nofollow">对象属性的枚举</a></p>