yuyue94 / blog Goto Github PK
View Code? Open in Web Editor NEWcooltag's blog
License: MIT License
cooltag's blog
License: MIT License
function get(id) {
return document.getElementById(id);
}
//第一个例子:不使用setTimeout
get('makeinput').onmousedown = function () {
var input = document.createElement('input');
input.setAttribute('type', 'text');
input.setAttribute('value', 'test1');
get('inpwrapper').appendChild(input);
input.focus();
input.select();
}
//第二个例子:使用setTimeout
get('makeinput2').onmousedown = function () {
var input = document.createElement('input');
input.setAttribute('type', 'text');
input.setAttribute('value', 'test1');
get('inpwrapper2').appendChild(input);
//setTimeout
setTimeout(function () {
input.focus();
input.select();
}, 0);
}
An event loop must continually run through the following steps for as long as it exists:
Run the oldest task on one of the event loop's task queues, if any, ignoring tasks whose associated Documents are not fully active. The user agent may pick any task queue.
If the storage mutex is now owned by the event loop, release it so that it is once again free.
If a task was run in the first step above, remove that task from its task queue.
If this event loop is not a worker's event loop, run these substeps:
Perform a microtask checkpoint.
Provide a stable state.
If necessary, update the rendering or user interface of any Document or browsing context to reflect the current state.
Otherwise, if this event loop is running for a WorkerGlobalScope, but there are no events in the event loop's task queues and the WorkerGlobalScope object's closing flag is true, then destroy the event loop, aborting these steps.
Return to the first step of the event loop.
以上大致意思是:执行task,释放储存锁,移除task,做microtask,渲染UI(其中的worker指的是web worker,web worker是实现的真正的多线程,一个线程就有一个event loop,所以在多线程的情况下如果没有事件,就要移除这个线程的event loop)
一个task的粒度是多大呢,microtask是什么,我们接下来讨论
(function test() {
// 1⃣️ task A 执行中
// 2⃣️ tasks 队列压入新的 task B
setTimeout(() => {
// microtasks 队列为空,于是检查 tasks 队列,取出 B并执行了
console.log(4)
}, 0)
new Promise(resolve => {
console.log(1)
for( var i=0 ; i<10000 ; i++ ) {
i == 9999 && resolve()
}
// 3⃣️ task A 继续执行
console.log(2)
})
// 4⃣️ microtasks 队列压入 microtask a
.then(() => {
// 6⃣️ microtask a 执行中
console.log(5)
Promise.resolve(7)
// 7⃣️ microtasks 队列压入 microtask b
.then(v => console.log(v))
// microtask a 执行完毕
})
// 8⃣️ microtasks 队列压入 microtask c
// 这个 then 执行完后继续检查 microtasks 队列,并一次执行 b,c
.then(() => {
console.log(6)
})
// 5⃣️ task A 执行完毕,检查 microtasks 队列,发现非空,执行 microtasks 队列的第一个 microtask a
console.log(3)
})()
/**
1
2
3
5
7
6
4
*/
观察者优先级
在每次轮训检查中,各观察者的优先级分别是:
idle观察者 > I/O观察者 > check观察者。
首先我们知道Object类型的转换和两个函数toString()和valueOf()有关,那为什么有关呢?
为了更容易讲清楚,我选择的是Standard ECMA-262 5.1 Edition,先从一个双操作数的加号操作符开始
var a = {};
a.toString = () => 100;
a.toString(); // 100;
String(a); // '100'
我们发现不同的操作符有不同的转换规则,所以笔者这里给大家分个类,这里我们列举常见的操作符,暂时不考虑位操作符,看完整的描述,可以从标准的这里开始看
// 普通的Object
var a = {}; // 普通类型的ToPrimitive会遵从hint Number的转换规则
a.toString = () => 100;
a.valueOf = () => '10';
a + 2; // '102' -> 相当于 '10' + 2 为 '102'
a + '2'; // '102'
a > 3; // true -> 进行ToPrimitive() hint为Number操作之后 -> 比较 '10' > 3 -> 不都是String类型,对'10'进行ToNumber(), 10 > 3为true
a > '3'; // false -> 实际比较的是 '10' > '3' 第一个字符的ascii码小,直接为false
a == 100; // false -> 相当于 '10' == 100为false
// Date类型的Object
var b = new Date(); // Date类型的ToPrimitive会遵从hint String的转换规则
b.toString = () => 100; // 这里的搞怪为了测试
b.valueOf = () => '10';
b + 2; // 102 -> 加号是ToPrimitive(), 100 + 2 为102
b + '2'; // '1002' -> 相当于 100 + '2'为'1002'
b > 3; // true -> 进行ToPrimitive() hint为Number操作之后 -> 比较 '10' > 3 -> 不都是String类型,对'10'进行ToNumber(), 10 > 3为true
b > '3'; // false -> 实际比较的是 '10' > '3' 第一个字符的ascii码小,直接为false
b == 100; // true -> 进行ToPrimitive()操作
// 有些你们神奇的测试姿势
{} + 1; // 1
// 有人会认为答案应该是'[object Object]1',因为对Object进行ToPrimary(),先看valueOf(),发现是自身,不是基本类型,再看toString(),发现是'[object Object]',返回这个值,然后再相加
// 但是问题出在编译器会认为前面的{}是一个代码块,后面是一元操作符加号和1,所以结果为1
{} + '1'; // 1
// 没错,这里证明了这个加号是一元的,将'1'转化为了number
var a = {};
a + 1; // '[object Object]1'
// 老铁,这次就对了
[1,2] + 1; // '1,21'
// [1,2],先对Object进行ToPrimary(),先看valueOf(),发现是自身,不是基本类型,再看toString,Array的toString()相当于join(','),所以得到'1,2'再和1相加得到'1,21'
es6中可以给对象设置一个属性,key是一个内置Symbol,value是个函数,函数中可以制定ToPrimary()的转换规则,key是Symbol.toPrimitive,也可以参考阮老师的es6入门-Symbol,以下代码是阮老师的代码,这里直接用了
let obj = {
[Symbol.toPrimitive](hint) {
switch (hint) {
case 'number':
return 123;
case 'string':
return 'str';
case 'default':
return 'default';
default:
throw new Error();
}
}
};
// 上方的三种hint就是我们在标准文档中看到的三种hint,根据hint的不同可以自定义return的值
2 * obj // 246 -> 乘号对应ToNumber(),先ToPrimitive(input argument, hint Number),再将基本值ToNumber()
3 + obj // '3default' -> 双操作符的加号对应ToPrimitive(),没有hint
obj == 'default' // true -> 双等号对应ToPrimitive(),没有hint
String(obj) // 'str' -> String对应ToString(),先ToPrimitive(input argument, hint String),再将基本值ToString()
// 实现一个add方法
function add() {
// ...
}
// 满足以下类型的调用和计算
add(1)(2)(3); // 6
var addTwo = add(2);
addTwo + 5; // 7
addTwo(3) + 5; // 10
add(4)(5) + add(2); // 11
function add(n){
var fn = function(x) {
return add(n + x);
};
fn.valueOf = function() {
return n;
};
return fn;
}
// cache是cache
// output是与组件双向绑定的数组
// toCache是请求获取到数据后的回调函数
// addLine是每次向outpuListShow数组加一条日志,组件即更新
let toCache = function(data) {
if (data && data.length > 0) {
[].push.apply(cache, data);
}
}
let addLine = function() {
let item = cache.shift();
if (item) {
output.push(item.line);
}
if (cache.length > 0) {
let frequency = Math.min(1 / cache.length * 1000, 100);
setTimeout(addLine, frequency);
}
}
<!-- vue template -->
<div id="log">
<div v-for="(line, index) in output" :key="index">
<span class="line-index">{{index}}</span>
<div v-if="line" class="log-line">
{{line}}
</div>
<br v-else/>
</div>
</div>
日志的总量可能十分庞大,可达几万行到十几万行
为了日志滚动输出的效果,会有组件刷新后滚动条自动沉底的操作,在运行时实时打日志和用户操作滚动条看已有日志会有冲突
单次来的日志量也可能很大,但也要保证滚动效果
方案1:减少日志组件的dom元素以减少回流性能损耗,比如只用一个div或者pre元素,只更新text来刷日志
方案2:运用滑动窗口,每次只显示固定数量的日志,一个窗口(tick)固定容量(capacity),当用户滚动条触顶新增可视窗口,类似于懒加载,但由于不是异步操作,速度很快,用户基本没有感觉
图片是所有问题中方案2的描述
var index = this.logWindow[this.logWindow.length - 1].index - this.capacity*this.tick;
if (index > 0 && this.logWindow.length < this.cache.length) {
this.tick++;
Vue.nextTick(() => {
document.getElementById('log-line-'+index).scrollIntoView();
});
}
e.target.scrollTop === 0
e.target.scrollTop + dom.clientHeight === dom.scrollHeight
if (silent) {
[].push.apply(output, intercept);
} else {
output = output.concat(intercept);
}
小技巧:按位非,按位与,按位或,按位异或,左移,右移都是对有符号整数的运算,var num = parseInt(str, 2)可以将一个二进制的字符串(比如str为'10101010001000101110101000101110',一定要为32位,可以自己补全)转为一个无符号整数2854414894,可以使用num | 0或者~~num或者 num >> 0等操作实现将num转为一个有符号整数-1440552402
// 都作为有符号整数来转化
// parseInt会保留符号,parseInt('-110', 2) => -6,不会通过第一个bit为来判断符号
var stringToInt32 = function(str){
// 默认str已经为32位
return parseInt(str, 2) | 0;
}
// toString(radix)会保留符号,所以(-2).toString(2) => '-10',而不是真正的int32的二进制表示
// 通过先转为Uint32再toString(2)是个不错的选择
var int32ToString = function(num){
// 保证返回的str是32位的
var zeroes32 = '00000000000000000000000000000000';
return (zeroes32 + (num >>> 0).toString(2)).slice(-32);
}
if(aa){} // 会报错,aa没有定义
if(this.aa){} //不会报错,视为当前对象没有aa这个属性,当成undefined处理
Infinity * 0; // NaN
Infinity / Infinity; // NaN
+0 === -0 // true
Object.is(+0, -0) // false
NaN === NaN // false
Object.is(NaN, NaN) // true
// 如何识别-0
// 方案1
var isNegZero = function(num) {
// The SameValue Algorithm
return Object.is(num, -0); // es6
}
// 方案2
var isNegZero = function(num) {
return num === 0 && 1 / num < 0; // 1/-0为-Infinity
}
// 方案3
var isNegZero = function(num) {
var obj = {};
// 默认4个Descriptor都为false
Object.defineProperty(obj, 'num', {value: -0});
try {
// The SameValue Algorithm
Object.defineProperty(obj, 'num', {value: num});
} catch(err) {
return false;
}
return true;
}
var num1=1, num2=2, num3=3;
var num = (5, 1, 3, 4, 0); // num值为0
- ECMAScript
- DOM
- BOM
A primitive (primitive value, primitive data type) is data that is not an object and has no methods. In JavaScript, there are 6 primitive data types: string, number, boolean, null, undefined, symbol (new in ECMAScript 2015).
All primitives are immutable (cannot be changed).
null == undefined; //true
null === undefined; //false
'' == false; //true
var obj = {};
obj == false; //false
var list = [];
list == false; //true
// 见最下面Object类型转换
if(obj){ // true
// 里面执行
console.log('yes');
}
if(list){ // true
// 里面执行
console.log('yes')
// list是一个Object对象,所以if(list)时是true。
}
var a = 0.1;
var b = 0.2;
a + b == 0.3 // false; a + b 结果等于0.30000000000000004
isNaN(NaN); //true
isNaN(10); //false (10是一个数值)
isNaN("10"); //false("10"可以被转换成数值10)
isNaN("blue"); //true (不能转换成数值)
isNaN(true); //false (可以被转换成数值1)
isNaN('123uuu'); //true(不能被Number()转化为数值)
isNaN(new Date()); //false (可以转换为百万毫秒的时间)
isNaN(new Date().toString()); //true (转化为string后就不行了)
isNaN(x) == isNaN(Number(x))
// true for every value of x, including x == undefined,
// because isNaN(undefined) == true and Number(undefined) returns NaN,
// but ...
isNaN() == isNaN(Number())
// false, because isNaN() == true and Number() == 0
Number.isNaN(NaN); //true
Number.isNaN(Number.NaN) //true
Number.isNaN(0/0) //true
Number.isNaN('NaN'); //false,是字符串不是NaN
Number.isNaN = Number.isNaN || function(value) {
return typeof value === "number" && isNaN(value);
}
// Or
Number.isNaN = Number.isNaN || function(value) {
return value !== value;
}
参考资料:(来自MDN)
isNaN()
Number.isNaN()
Number(true) = 1;
Number(false) = 0;
Number(null) = 0;
Number(undefined) = NaN;
Number({}) = NaN;
Number([]) = 0;
Number('') = 0;
parseInt('') = NaN; // 没有能合法提取的
parseInt('22.5') = 22;
parseInt('123uuu') = 123;
var num = 10;
num.toString(); // '10'
num.toString(2); // '1010'
num.toString(8); // '12'
num.toString(16); // 'a'
Object的每个实例都具有下列属性和方法
var a = {};
a.valueOf = () => '1';
a.toString = () => 2;
Number(a) // 1 ==> Number(a.valueOf())
+a //1 同上
a + a // '11' ==> a转原始值先走valueOf() -> a.valueOf() + a.valueOf()
String(a) // '2'==> String(a.toString())
var obj = {};
Number(obj) // NaN ==> a.valueOf()为自身不是原始值 -> a.toString()为'[object Object]'是原始值 -> '[object Object]'转Number为NaN
obj + obj // '[object Object][object Object]'
var b = new Date();
Number(b) // 1499505510372
+b // 1499505510372
b + b // 'Sat Jul 08 2017 17:25:35 GMT+0800 (CST)Sat Jul 08 2017 17:25:35 GMT+0800 (CST)' ==> Date类型转原始值先走toString()
String(b) // 'Sat Jul 08 2017 17:25:35 GMT+0800 (CST)'
var c = [];
Number(c) // 0 ==> c.valueOf()为自身不是原始值 -> c.toString()为'' -> ''转Number为0
c + c // '' ==> c转原始值为'' -> '' + ''为''
var d = [1,2];
Number(d) // NaN ==> d.valueOf()为自身不是原始值 -> d.toString()为d.join(',')为'1,2' -> '1,2'转Number为NaN
d + d // '1,21,2'
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.