目录
- 边边角角的知识
- 1. in
- 2. slice
- 3. isFinite
- 4. 复制数组
- 5. 将类似数组的对象转为数组--`Array.from`
- 6. URL
- 7. a标签的download属性采坑
- 8. 使用 download 属性保存画布为PNG格式
- 9. ES6 import 循环引用
- 10. null、undefined、""、0
- 11. chrome输入框autofill黄色背景
- 12. setTimeout 0
- 13. [JS模块化](https://zhuanlan.zhihu.com/p/24098482?refer=youngz)
- 14. [跨域cookie](http://zawa.iteye.com/blog/1868108)
- 15. 关于浏览器模式的那些事儿
- 16. input、change、propertychange区别
- 17. [从Chrome源码看浏览器的事件机制](https://zhuanlan.zhihu.com/p/25095179?refer=dreawer)
- 18. [`__proto__`与 `prototype` 的爱恨情仇](http://blog.csdn.net/AmberDeng/article/details/78457347)
- 19. `$.attr()`和`$.prop()`
- 20. 判断自然数(>=0整数)
- 21. 数组最大/小值
- 22. jquery事件绑定
- 23. form nest
- 24. exports、module.exports
- 25. for 中的 let
- 26. HTTP 协议
- 27. [36个代码块,带你读懂异常处理的优雅演进](https://mp.weixin.qq.com/s?__biz=MzIwNjQwMzUwMQ==&mid=2247484976&idx=1&sn=af2a8b2cabdef9f9396120ca1dd0eae5&chksm=972364f2a054ede406670bf591e0723655c207994a92f7620d4392b66c467610d4be55feab9d&scene=21#wechat_redirect)
- 28.[JavaScript 运行机制详解:再谈Event Loop](http://www.ruanyifeng.com/blog/2014/10/event-loop.html)
- 29. [ES7 Decorator 装饰者模式](http://taobaofed.org/blog/2015/11/16/es7-decorator/)
- 30. JSON.parse(string) => Number 可能会溢出
- 31. post上传文件Content-type
- 32. module.exports 和 export default 不能混用
- 33. [viewport to read](https://www.quirksmode.org/mobile/viewports2.html)
- 34. [window.performance](http://www.alloyteam.com/2015/09/explore-performance/)
- 35. 如何使得`a === 1 && a === 2 && a === 3`?
- 36. [Proxy](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy)
- 37. concat
- 38. 循环
- 38. decodeURIComponent % 报错
- 39. 正则[]
- 40. var、let、const
- 41. sort 参数顺序
- 42. [set、map、weakSet、weakMap](http://es6.ruanyifeng.com/#docs/set-map)
- 43. 包装对象 Boolean
- 困惑:
边边角角的知识
1. in
数组
// 数组
var trees = new Array("redwood", "bay", "cedar", "oak", "maple");
0 in trees // 返回true
3 in trees // 返回true
6 in trees // 返回false
// -------------------------attention----------------------
"bay" in trees // 返回false (必须使用索引号,而不是数组元素的值)
// -------------------------attention----------------------
"length" in trees // 返回true (length是一个数组属性)
delete和undefined
var mycar = {make: "Honda", model: "Accord", year: 1998};
delete mycar.make;
"make" in mycar; // 返回false
var mycar = {make: "Honda", model: "Accord", year: 1998};
mycar.make = undefined;
"make" in mycar; // 返回true
2. slice
可以不指定任何参数,返回原数组
3. isFinite
不太懂为什么???????
isFinite("0"); // true
Number.isFinite("0"); // false
4. 复制数组
// good
const itemsCopy = [...items];
5. 将类似数组的对象转为数组–Array.from
const foo = document.querySelectorAll('.foo');
const nodes = Array.from(foo);
6. URL
编码原因: Url中有些字符会引起歧义
编码格式:ASCII码
编码方式: %加上两位十六进制字符
encodeURI作对完整的URI进行编码,encodeURIComponent对URI的一个组件进行编码,所以encodeURI的安全字符更多,编码范围小。
url的三个js编码函数escape(),encodeURI(),encodeURIComponent()简介
网络标准RFC 1738做了硬性规定:
“只有字母和数字[0-9a-zA-Z]、一些特殊符号“$-_.+!*’(),”[不包括双引号]、以及某些保留字,才可以不经过编码直接用于URL。”
-
escape()不能直接用于URL编码,它的真正作用是返回一个字符的Unicode编码值。比如"春节"的返回结果是%u6625%u8282,escape()不对"+"编码(网页在提交表单的时候,如果有空格,则会被转化为+字符。服务器处理数据的时候,会把+号处理成空格。),主要用于汉字编码,现在已经不提倡使用。
-
encodeURI()是Javascript中真正用来对URL编码的函数。 编码整个url地址(编码后,它输出符号的utf-8形式,并且在每个字节前加上%),但对特殊含义的符号"; / ? : @ & = + $ , #"以及单引号,不进行编码。对应的解码函数是:decodeURI()。
-
encodeURIComponent() 能编码"; / ? : @ & = + $ , #"这些特殊字符。对应的解码函数是decodeURIComponent()。
7. a标签的download属性采坑
file方式打开无效,而且链接是http协议的无效
8. 使用 download 属性保存画布为PNG格式
9. ES6 import 循环引用
10. null、undefined、""、0
null == "" // false
undefined == "" // false
null == 0 // false
undefined == 0 // false
Number(null) // 0
Number(undefined) // NaN
"" == 0 // true
关于==
和===
绝大多数场合应该使用 === ,只有检测 null/undefined 的时候可以使用 x == null ,因为通常我们不区分 null 和 undefined ,即将 x == null 作为 x === null || x === undefined 的缩写。
11. chrome输入框autofill黄色背景
input:-webkit-autofill 导致chrome的输入框背景颜色变成黄色
<input type="text" autocomplete="off">
并没有什么卵用- 还是乖乖用
box-shadow
吧
input:-webkit-autofill, textarea:-webkit-autofill, select:-webkit-autofill {
-webkit-box-shadow: 0 0 0 1000px white inset;
}
12. setTimeout 0
How JavaScript Timers Work
setTimeout 的执行时间会大于(等待前一个函数执行完)等于设置时间,setInterval 则不一定,两次 interval 之间可能没有间隔,也可能某次被 drop 掉
Promise的队列与setTimeout的队列的有何关联?【优先级问题】
浏览器的内核是多线程的,它们在内核制控下相互配合以保持同步,一个浏览器至少实现三个常驻线程:javascript引擎线程,GUI渲染线程,浏览器事件触发线程。
- javascript引擎是基于事件驱动单线程执行的,JS引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JS线程在运行JS程序。
- GUI渲染线程负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。但需要注意 GUI渲染线程与JS引擎是互斥的,当JS引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
- 事件触发线程,当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理。这些事件可来自JavaScript引擎当前执行的代码块如setTimeOut、也可来自浏览器内核的其他线程如鼠标点击、AJAX异步请求等,但由于JS的单线程关系所有这些事件都得排队等待JS引擎处理。(当线程中没有执行任何同步代码的前提下才会执行异步代码)
13. JS模块化
- commonJS
// 暴露
module.exports = myModule;
// 引用s
var myModule = require('myModule');
- AMD
define(['myModule'], function(myModule) {
return something;
});
- UMD
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['myModule', 'myOtherModule'], factory);
} else if (typeof exports === 'object') {
// CommonJS
module.exports = factory(require('myModule'), require('myOtherModule'));
} else {
// Browser globals (Note: root is window)
root.returnExports = factory(root.myModule, root.myOtherModule);
}
}(this, function (myModule, myOtherModule) {
// Methods
function notHelloOrGoodbye(){}; // A private method
function hello(){}; // A public method because it's returned (see below)
function goodbye(){}; // A public method because it's returned (see below)
// Exposed public methods
return {
hello: hello,
goodbye: goodbye
}
}));
14. 跨域cookie
15. 关于浏览器模式的那些事儿
- 怪异模式 会模拟IE,同时CSS解析会比较宽松,例如数字单位可以省略
- 怪异模式下的input和textarea的默认盒模型将会变成border-box:
- 标准模式下的文档高度是实际内容的高度:
而在怪异模式下的文档高度是窗口可视域的高度:
- 有限怪异模式 和 标准模式 的唯一区别在于在于对inline元素的行高处理不一样。
- 有限怪异模式下,div里面的图片下方不会留空白,如下图一所示;而在标准模式下div下方会留点空白,如下图二所示:
这个空白是div的行高撑起来的,当把div的行高设置成0的时候,就没有下面的空白了。在怪异模和有限怪异模式下,为了计算行内子元素的最小高度,一个块级元素的行高必须被忽略。
- 标准模式 将会让页面遵守文档规定
DOM树有一个最大的深度:maximumHTMLParserDOMTreeDepth,超过这个最大深度就会把它子元素当作父无素的同级节点,这个最大值是多少呢?512
16. input、change、propertychange区别
总结oninput、onchange与onpropertychange事件的用法和区别
-
change事件:
事件触发取决于表格元素的类型(type)和用户对标签的操作:<input type="radio">
和<input type="checkbox">
的默认选项被修改时(通过点击或者键盘事件)
【IE(LTE8)中点击、失去焦点后才会触发,见 js change,propertychange,input事件小议】;- 当用户完成提交动作时 (例如:点击了
<select>
中的一个选项,从<input type="date">
标签选择了一个日期,通过<input type="file">
标签上传了一个文件,等 ); - 当标签的值被修改并且失焦后,但并未进行提交 (例如:对
<textarea>
或者<input type="text">
的值进行编辑后。).
-
input事件(IE9+):
- 在value改变时实时触发,js改变value时不会触发;
<select>
不会触发- 通过addEventListener()注册
-
- 当input设置为disable=true后,propertychange不会触发。
-
input 和 propertychange在 IE9 中都有个小BUG:通过右键菜单菜单中的 剪切 和 删除 命令删除内容的时候不会触发
17. 从Chrome源码看浏览器的事件机制
- 事件回收:
删掉一个DOM结点,不需要手动去释放它的事件,但是DOM结点一旦存在一个引用,即使你把它remove掉了,GC也不会去回收,如下:
<script>
var p = document.getElementById("text");
p.remove();
window.gc();
</script>
执行了window.gc之后并不会去回收p的内存空间以及它的事件。因为还存在一个p的变量指向它,而如果将p置为null,如下:
<script>
var p = document.getElementById("text");
p.remove();
p = null;
window.gc();
</script>
最后的GC就管用了,或者p离开了作用域:
<script>
!function(){
var p = document.getElementById("text");
p.remove();
}()
window.gc();
</script>
自动销毁,p结点没有人引用了,能够自动GC回收。
18. __proto__
与 prototype
的爱恨情仇
19. $.attr()
和$.prop()
- attr
- DOM元素所对应的文档节点上的属性(attribute)
- 设置的属性值只能是字符串类型
- attribute的checked、selected、disabled就是表示该属性初始状态的值
- prop对应着js对象
- DOM元素(指的是JS对象,Element类型)上的属性(property)
- 设置的属性值可谓为任意类型
- property的checked、selected、disabled才表示该属性实时状态的值(值为true或false)
20. 判断自然数(>=0整数)
if (!n || typeof n !== 'number' || !isFinite(n) || n < 0 || Math.floor(n) !== Math.ceil(n)) {
console.warn(`argument n: ${n} is not a natural number`);
}
Object.prototype.toString.call(NaN) => '[object Number]'
21. 数组最大/小值
Math.min.apply(null, array)
Math.max.apply(null, array)
【数组中不能有无法转化为数字的元素,否则会返回NaN】
Math.max.apply(null, [3,2,5,7,,3,,,9,7,87,78]) => NaN
22. jquery事件绑定
-
参考:
-
结论:
- jQuery利用
jQuery.event.on
给元素构造了events
和handle
属性,一次性addEventListener
,事件触发则执行handle
调用jQuery.event.dispatch
调取绑定的事件函数events[event.type]
进行操作。 addEventListener
会去重,所以不断绑定新的函数时会受已绑定的函数数量影响;如果绑定的是相同的函数,addEventListener
效率会高不少。
- jQuery利用
-
测试代码:
console.time('jq');
for(let i = 0; i < 10000; i++){
$('.food_pic').on('click', function () {
console.log(33333)
});
}
console.timeEnd('jq');
console.time('js');
for(let i = 0; i < 10000; i++){
$('.food_pic')[0].addEventListener('click', function () {
console.log(4444444)
});
}
console.timeEnd('js');
=============================>
// jq: 109.872ms
// js: 260.357ms
// 10000次 33333
// 10000次 4444444
var i;
function test1() {console.log(11111111)};
function test2() {console.log(22222222)};
console.time('jq');
for (i = 0; i < 10000; i += 1) $(document.querySelector('.food_pic')).on('click', test);
console.timeEnd('jq');
console.time('js');
for (i = 0; i < 10000; i += 1) document.querySelector('.food_pic').addEventListener('click', test);
console.timeEnd('js');
=============================>
// jq: 152.260ms
// js: 49.812ms
// 10000次 11111111
// 22222222
23. form nest
<form>
不能嵌套,<ng-form>
可以
24. exports、module.exports
module.exports 初始值为一个空对象 {}
exports 是指向的 module.exports 的引用
require() 返回的是 module.exports 而不是 exports
避免module.exports 指向新的对象时,exports 断开了与 module.exports 的引用
exports = module.exports = somethings
// 等价于:
module.exports = somethings
exports = module.exports
25. for 中的 let
for (let x…)的循环在每次迭代时都为x创建新的绑定
for (let i = 0; i < 3; i++) {
setTimeout(() => {console.log("in for block", i)}, 1000);
}
// in for block 0
// in for block 1
// in for block 2
for 的 {}是 for () 的子作用域
for (let i = 0 /* 作用域a */; i < 3; console.log("in for expression", i), i++) {
let i; //这里没有报错,就意味着这里跟作用域a不同,换做k可能更好理解
console.log("in for block", i);
}
// in for block undefined
// in for expression 0
// in for block undefined
// in for expression 1
// in for block undefined
// in for expression 2
26. HTTP 协议
HTTP 1.1 管道机制:同一个TCP连接里,允许浏览器同时发出A请求和B请求,但是服务器还是按照顺序,先回应A请求,完成后再回应B请求
HTTP 1.0 Content-length: 用于区分数据包属于哪一个回应【因为管道机制可以一个TCP传多个回应】
HTTP 1.1 分块传输编码Transfer-Encoding: chunked
: 每个非空的数据块前有一个16进制的数值,表示这个块的长度。最后是一个大小为0的块,就表示本次回应的数据发送完了。
- POST Content-Type
- application/x-www-form-urlencoded(默认): 数据被编码成以 ‘&’ 分隔的键-值对, 同时以 ‘=’ 分隔键和值. 非字母或数字的字符会被 percent encoded: 这也就是为什么这种类型不支持二进制数据的原因 (应使用 application/form-data 代替).
- application/form-data
- text/plain
- HTTP cookies 详解
27. 36个代码块,带你读懂异常处理的优雅演进
阶段 | 备注 |
---|---|
回调 | 同步、异步 |
Promise | resolve:then 的第一个回调会立即插入 microtask 队列,异步立即执行 reject:那么 then 的第二个回调会立即插入 microtask 队列 抛出的异常和未捕获的 reject 会传到末尾,通过 catch 接住 resolve 决议会被自动展开(reject 不会) 链式流,then 会返回一个新的 Promise,其状态取决于 then 的返回值 macrotask 中 throw 的 error 无法捕获【指setTimeout等】(reject 可以)、microtask 可以 |
Async Await | async await 是 generator 的语法糖 await 不会自动捕获异常,但是会自动中断函数 try catch 进行异常捕获 macrotask 中 throw 的 error 无法捕获【指setTimeout等】(reject 可以)、microtask 可以 |
Decorator 统一异常捕获 | 异常冒泡 |
兜底 | Node.js:监听全局错误 uncaughtException、unhandleRejection 浏览器:监听 window 全局错误 unhandlerejection、onrejectionhandled |
28.JavaScript 运行机制详解:再谈Event Loop
- setTimeout 在**"任务队列"的尾部**添加一个事件,因此要等到同步任务和"任务队列"现有的事件都处理完,才会得到执行。
- HTML5标准规定了setTimeout()的第二个参数的最小值(最短间隔),不得低于4毫秒,如果低于这个值,就会自动增加。
- node process.nextTick方法可以在当前"执行栈"的尾部----下一次Event Loop(主线程读取"任务队列")之前----触发回调函数。
- node setImmediate 下次"Event Loop"触发,其和 setTimeout(fn,0) 执行顺序不确定
29. ES7 Decorator 装饰者模式
- 适配器模式我们使用的场景比较多,比如连接不同数据库的情况,你需要包装现有的模块接口,从而使之适配数据库 —— 好比你手机使用转接口来适配插座那样;
- 装饰模式不一样,仅仅包装现有的模块,使之 “更加华丽” ,并不会影响原有接口的功能 —— 好比你给手机添加一个外壳罢了,并不影响手机原有的通话、充电等功能;
30. JSON.parse(string) => Number 可能会溢出
31. post上传文件Content-type
需删除content-type,这样才会正确的结束boundary
32. module.exports 和 export default 不能混用
问题在于export default会利用babel编译
(function(module, exports) {
exports.A= 674;
}),
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "B", function() { return B; });
exports.A= 674;
var B= 1;
}),
33. viewport to read
34. window.performance
Navigation Timing
35. 如何使得a === 1 && a === 2 && a === 3
?
var val = 0
Object.defineProperty(window, 'a', {
get: () => ++val
})
a === 1 && a === 2 && a === 3 && console.log('perfect?')
36. Proxy
如何使得console.log('c' in p, p.c); // false, 37
?
let handler = {
get: function(target, name){
return name in target ? target[name] : 37;
}
};
let p = new Proxy({}, handler);
37. concat
concat方法创建一个新的数组,原始数组和新数组都引用相同的对象。
var num1 = [[1]];
var num2 = [2, [3]];
var nums = num1.concat(num2);
console.log(nums);
// results in [[1], 2, [3]]
// modify the first element of num1
num1[0].push(4);
console.log(nums);
// results in [[1, 4], 2, [3]]
38. 循环
- forEach 相当于 for (i = 0, len = test.length; i < len; i++),循环时无影响
- for in 以任意序迭代一个对象的可枚举属性,遍历过程中的修改在之后的遍历具有不确定性
- 不保证是否一个被添加的属性在迭代过程中会被访问到
- 不保证一个修改后的属性(除非是正在被访问的)会在修改前或者修改后被访问
- 不保证一个被删除的属性将会在它被删除之前被访问
// 改变数组本身的循环测试
var test = [1]
for (i = 0; i < test.length; i++) {
console.log('--------i', i)
test.push(i + 1)
if (i >= 5) {
break
}
}
// --------i 0
// --------i 1
// --------i 2
// --------i 3
// --------i 4
// --------i 5
var test = [1]
for (i of test) {
console.log('--------i', i)
test.push(i + 1)
if (i >= 5) {
break
}
}
// --------i 1
// --------i 2
// --------i 3
// --------i 4
// --------i 5
var test = [1]
test.forEach(function(i) {
console.log('--------i', i)
test.push(i + 1)
if (i >= 5) {
return
}
})
// --------i 1
38. decodeURIComponent % 报错
decodeURIComponent("%") ----->Uncaught URIError: URI malformed
decodeURIComponent("%25") ----->%
39. 正则[]
/^(https|http):$/
全匹配http:
或者https:
/^[https:|http:]$/
实际匹配/^[htps:|]$/
40. var、let、const
Referer: 25
var | let、const |
---|---|
没有块级作用域 | 有块级作用域 |
41. sort 参数顺序
var c = [3, 6, 2, 8]
c.sort(function(a, b) {console.log(a, b)}) // a为后面一个,b为前面一个
// 6 3
// 2 6
// 8 2
42. set、map、weakSet、weakMap
- set
- 类似数组,但是成员的值都是唯一的
- 应用:
// 去重
[...new Set(array)]
Array.from(new Set(array))
[...new Set('ababbc')].join('') // "abc"
-
map
- 类似于对象,但是“键”的范围不限于字符串
-
weakSet:
- 成员只能是对象
- 弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用
- 不可遍历
- 应用:
// 保证了Foo的实例方法,只能在Foo的实例上调用
const foos = new WeakSet()
class Foo {
constructor() {
foos.add(this)
}
method () {
if (!foos.has(this)) {
throw new TypeError('Foo.prototype.method 只能在Foo的实例上调用!');
}
}
}
- weakMap
- 只接受对象作为键名(null除外)
- 键名(非键值)所指向的对象,不计入垃圾回收机制
- 不可遍历、无法清空
- 应用:DOM作为键值、部署私有属性
43. 包装对象 Boolean
var a = new Boolean(false);
var b = a && true;
console.log(b); //true
困惑:
- 如何在调用函数时控制流程(出错则不继续执行余下代码,实际类似于return透传)(例如函数需要上下文中的参数,但是希望不满足条件就不执行余下外围代码)