目录
A.专用:setTimeout(functionRef, delay, param...)
b.let/forEach():每次迭代i是新变量,新块级作用域
await xx;yy=promise(xx).then(yy)微任务
new promise 初始为pengding,不存在为null的情况
return error≠throw error,所以不会被捕获错误
- var F = function() {};
-
- Object.prototype.a = function() {
- console.log('a');
- };
-
- Function.prototype.b = function() {
- console.log('b');
- }
-
- var f = new F();
-
- f.a(); // a
- f.b(); // f.b is not a function
-
- F.a(); // a
- F.b(); // b
Function.prototype === Function.__proto__; // true
Function本身也是函数,所以 Function是Function的实例
注:箭头表继承
Object⬅️F(F类的构造函数)⬅️f(f是F类实例)
⬆️
Function
⬆️
F(Function实例)
由函数Function实例F创建的实例f不再是函数Function
- var a=1;
- function fn1(){
- var a=2;
- console.log(this.a+a);
- }
-
- var fn3=function(){
- this.a=3;
- }
- //自身找不到才会去原型链上找,可以理解为替补
- fn3.prototype={
- a:4
- }
- var fn33=new fn3();
- fn1.call(fn33)
- //5
- function A() {}
- function B(a) {
- this.a = a;
- }
- function C(a) {
- if (a) {
- this.a = a;
- }
- }
- A.prototype.a = 1;
- B.prototype.a = 1;
- C.prototype.a = 1;
-
- console.log(new A().a); //1
- console.log(new B().a); //undefined(传入a为undefined)
- console.log(new C(2).a);//2
- console.log(new C().a); //1
原型链123['toString']:在数字 123 上使用方括号访问属性
数字本身没有toString方法,则沿着__proto__去function Number()的prototype上找,找到toString方法,toString方法的length是1
numObj.toString([radix])
console.log(123['toString'].length + 123) // 124
- function fun(){
- this.a = 0
- this.b = function(){
- console.log("自己的b:",this.a)
- }
- }
-
- fun.prototype = {
- b: function(){
- this.a = 20
- console.log("原型链b:",this.a)
- },
- c: function (){
- this.a = 30
- console.log(this.a)
- }
- }
-
- var my_fun = new fun()
-
- my_fun.b() // 0
- my_fun.c() // 30
- function Foo() {
- getName = function (){
- console.log(1)
- }
- return this
- }
-
- Foo.getName = function () {
- console.log(2)
- }
-
- Foo.prototype.getName = function(){
- console.log(3)
- }
-
- Foo.getName()//2
-
- Foo().getName();//1
-
- getName();//1:getName函数变量提升到全局
-
- new Foo.getName()//2 Foo函数有对象有个getName(...2)属性方法
- //先对 new Foo() 实例化再对 A.getName() 调用,
- //对 new Foo() 实例化调用的 getName() 方法是原型 prototype 上的
- new Foo().getName()//3
- // new new Foo().getName() => new B.getName(),
- //先对 new Foo() 实例化再对 new B.getName() 实例化,
- //new B.getName() 同时也在执行 B.getName() 方法输出的还是实例 B 上的方法也就是原型 prototype 的 getName
- new new Foo().getName()//3
f1,f2中本没有 constructor 但是会从构造函数的 prototype 中查找相当f1.prototype.constructor,f2的原型被重新定义了指向基类 object
找不到的,只会往上找,而非往下,所以原型上不存在n
- function Fn(){
- var n = 10
- this.m = 20
- this.aa = function() {
- console.log(this.m)
- }
- }
-
- Fn.prototype.bb = function () {
- console.log("原型的this.n",this.n)
- }
-
- var f1 = new Fn
-
- Fn.prototype = {
- aa: function(){
- console.log(this.m + 10)
- }
- }
-
- var f2 = new Fn
-
- //注意区别修改原型Fn.prototype和修改原型的属性Fn.prototype.bb
- console.log(f1.constructor) // ==> function Fn(){...}
- console.log(f2.constructor) // ==> Object() { [native code] }
-
- //原型中
- f1.bb() // n是 undefined
- //自己有aa方法,就不去原型链上找了
- f1.aa() // 20
- f2.aa() // 20
- //原型链上的aa方法中,原型没有m属性,undefined+10=NaN
- f2.__proto__.aa() // NaN
- f2.bb() // Uncaught TypeError: f2.bb is not a function
默认参数:第一个具有默认值之前的参数个数- function fn1 (name) {}
-
- function fn2 (name = '林三心') {}
-
- function fn3 (name, age = 22) {}
-
- function fn4 (name, age = 22, gender) {}
-
- function fn5(name = '林三心', age, gender) { }
-
- console.log(fn1.length) // 1
- console.log(fn2.length) // 0
- console.log(fn3.length) // 1
- console.log(fn4.length) // 1
- console.log(fn5.length) // 0
length- function fn1(name, ...args) {}
-
- console.log(fn1.length) // 1
- var ary = [1, 2, 3, 4]
- function fn(i){
- return function(n){
- console.log(n+ (i++))
- }
- }
-
- var f = fn(10)
- f(20) // 30 (n+10)
- //fn(10)被f引用,未被释放
- f(20) // 31 (n+11)
-
- //就算都是fn(10),也只是函数体相同但地址不同的两个函数
- fn(10)(20) // 30
- //fn(10)执行完后就释放了
- fn(10)(20) // 30
-
- // console.log(i) // Uncaught ReferenceError: i is not defined
- function createMultiplier(factor) {
- // 返回一个新函数,这个函数将传入的参数与 factor 相乘
- return function (number) {
- return number * factor;
- };
- }
-
- // 创建一个乘以 2 的函数
- const double = createMultiplier(2);
-
- // 创建一个乘以 3 的函数
- const triple = createMultiplier(3);
-
- // 使用这些函数
- console.log(double(5)); // 输出 10,因为 5 * 2 = 10
- console.log(triple(5)); // 输出 15,因为 5 * 3 = 15
- var num = 10 // 60; 65
- var obj = {
- num: 20
- }
- //自执行obj.fn= function(n){this.num+=n...}
- obj.fn = (function (num){
- this.num = num * 3 // 调用者是window,window.num=20*3
- num++ // 21
- return function(n){
- this.num += n // 60 + 5 = 65;20 + 10 =30
- num++ // 21 + 1 = 22;22 + 1 = 23 闭包引用num
- console.log(num)
- }
- })(obj.num)
-
- var fn = obj.fn
-
- fn(5) // 22 this 指向 window
-
- obj.fn(10) // 23 this 指向 obj
-
- console.log(num, obj.num) // 65, 30
- this.count=1
- function func() {
- console.log(++this.count)
- }
-
- func.count = 0
- func()//2
-
- obj = {
- func() {
- //箭头函数继承调用func()的this
- const arrowFunc = () => {
- console.log(this._name)
- }
-
- return arrowFunc
- },
-
- _name: "obj",
- }
-
- //仅赋予函数体内容
- func = obj.func
- //调用者为全局,全局下没有“_name”属性
- func()()//undefined
-
- obj.func()()//obj
- //bind绑定但不调用
- obj.func.bind({ _name: "newObj" })()()//newObj
- //apply绑定并调用
- obj.func.apply({ _name: "newObj" })()//newObj
- //传入空this
- obj.func.bind()()()//undefined
- //无论这个新函数如何被调用,this 被永久绑定到 { _name: "bindObj" }
- obj.func.bind({ _name: "bindObj" }).apply({ _name: "applyObj" })()//bindObj
- //虽然按顺序创建作用域,不会报错a为声明
- function foo() {
- console.log(a);
- }
-
- function bar() {
- var a="bar"
- foo();
- }
-
- bar(); //undefined
- var a="window"
- var a = 1;
- function foo(a, b) {
- console.log(a); // 1
- a = 2;
- arguments[0] = 3;
- var a;
- console.log(a, this.a, b); // 3, 1, undefined
- }
- foo(a);
- function test() {
- var foo = 33;
- if (foo) {// var foo
- //foo+55是let 的 foo
- let foo = foo + 55; // ReferenceError
- }
- }
- test();
标识符 n.a 被解析为位于指令(let n)本身的 n 对象的属性 a。因为 n 的声明尚未执行结束,它仍然处于暂时性死区内
- function go(n) {
- // n 在此处被定义
- console.log(n); // { a: [1, 2, 3] }
-
- for (let n of n.a) {
- // ^ ReferenceError
- console.log(n);
- }
- }
-
- go({ a: [1, 2, 3] });
= 优先级是从右到左的,所以变量提升阶段 b=undefined后,将 c 赋值成 undefined
- var b = {
- a,
- c: b
- }
- console.log(b.c);
- //undefined
- for(var i=0;i<2;i++){
- setTimeout(()=>{console.log(i)},1000)//3,3
- }
- for(var i=0;i<3;i++){
- setTimeout(()=>{console.log(i)},1000)//3,3,3
- }
- console.log(i)//3
- // 利用setTimeout的第三个参数,第三个参数将作为setTimeout第一个参数的参数
- for (var i = 0; i < 5; i++) {
- setTimeout(function fn(i) {
- console.log(i);
- }, 1000, i); // 第三个参数i,将作为fn的参数
- }
- //输出0,1,2,3,4
- //立即执行函数传参,就不会引用var i了
- for (var i = 0; i < 5; i++) {
- (function(j) {
- setTimeout(function timer() {
- console.log(j);
- }, 1000);
- })(i);
- //等效于
- setTimeout((function(i){
- return () => console.log(i);
- })(i),1000)
- }
- for(let i=0;i<2;i++){
- setTimeout(()=>{console.log(i)},1000)//0,1
- }
- //宏任务队列:[]
- //微任务队列:[promise0]
- Promise.resolve()
- .then(function() {
- console.log("promise0");
- })
- .then(function() {
- console.log("promise5");
- });
- //定时的setTimeout(delay=0)=setImmediate:下个Event Loop执行
- //宏任务队列:[timer1]
- //微任务队列:[promise0]
- setTimeout(() => {
- console.log("timer1");
-
- Promise.resolve().then(function() {
- console.log("promise2");
- });
- Promise.resolve().then(function() {
- console.log("promise4");
- });
- }, 0);
- //宏任务队列:[timer1,timer2]
- //微任务队列:[promise0]
- setTimeout(() => {
- console.log("timer2");
- Promise.resolve().then(function() {
- console.log("promise3");
- });
- }, 0);
- //宏任务队列:[timer1,timer2]
- //微任务队列:[promise0,promise1]
- Promise.resolve().then(function() {
- console.log("promise1");
- });
- //执行start
- console.log("start");
- //执行当前所有微任务队列:[promise0,promise1]
- //执行promise0时将promise5放入了微任务队列:[promise1,promise5]
- //接着执行微任务队列:输出promise1,promise5
- //当微任务队列为空,开始执行宏任务队列[timer1,timer2]队首的timer1
- //执行timer1时碰到了微任务promise2,放进微任务队列[promise2]
- //宏任务timer1执行完了,开始执行所有当前所有微任务:[promise2]
- //执行promise2完碰到微任务promise4,放进微任务队列:[promise4]
- //当前微任务队列不为空,接着执行promise4
- //微任务队列为空,接着执行宏任务队列队首[timer2]
- //执行timer2时碰到了微任务promise3,放进微任务队列[promise3]
- //宏任务timer2执行完了,开始执行所有当前所有微任务:[promise3]
-
-
- // 打印结果: start promise0 promise1 promise5 timer1 promise2 promise4 timer2 promise3
-
- //1.script start(同步)
- console.log("script start");
-
- async function async1() {
- await async2(); // await 隐式返回promise
- console.log("async1 end"); // 这里的执行时机:在执行微任务时执行
- }
-
- async function async2() {
- console.log("async2 end"); // 这里是同步代码
- }
- //2.async2 end(同步)
- //微任务队列:[async1 end]
- async1();
- //宏任务队列:[setTimeout],setTimeOut进入下一loop
- setTimeout(function() {
- console.log("setTimeout");
- }, 0);
- //3.Promise(同步)
- //宏任务队列:[setTimeout]
- //微任务队列:[async1 end,promise1]
- new Promise(resolve => {
- console.log("Promise"); // 这里是同步代码
- resolve();
- })
- .then(function() {
- console.log("promise1");
- })
- .then(function() {
- console.log("promise2");
- });
- //4.script end(同步)
- console.log("script end");
- //当前loop的宏任务(都是同步代码)都执行完毕
- //执行所有微任务[async1 end,promise1]
- //执行promise1完后碰到了promise2,加入微任务队列,接着执行
- //当前所有微任务都执行完毕,开始执行宏任务队列[setTimeout]
-
- // 打印结果: script start => async2 end => Promise => script end => async1 end => promise1 => promise2 => setTimeout
- async function async1 () {
- console.log('async1 start');
- await new Promise(resolve => {
- console.log('promise1')
- })
- console.log('async1 success');
- return 'async1 end'
- }
- console.log('srcipt start')
- async1().then(res => console.log(res))
- console.log('srcipt end')
-
- > "srcipt start"
- > "async1 start"
- > "promise1"
- > "srcipt end"
- console.log("start");
- //定时进入下一loop,宏任务队列:[timeout]
- setTimeout(() => {
- console.log("timeout");
- }, 0);
- //微任务队列:[promise]
- Promise.resolve().then(() => {
- console.log("promise");
- });
- //process.nextTick在微任务队首
- //微任务队列:[nextTick,promise]
- process.nextTick(() => {
- console.log("nextTick");
- Promise.resolve().then(() => {
- console.log("promise1");
- });
- });
- console.log("end");
- // 执行结果 start end nextTick promise promise1 timeout
1.pengding时,then()收集依赖,将成功/失败回调放入成功/失败队列
2.触发resolve/reject,改变pending,从成功/失败队列中取出回调依次执行
- const promise = new Promise((resolve, reject) => {
- console.log(1);
- console.log(2);
- });
- promise.then(() => {
- console.log(3);
- });
- console.log(4);
- //124
- const promise1 = new Promise((resolve, reject) => {
- console.log('promise1')
- resolve('resolve1')
- })
- const promise2 = promise1.then(res => {
- console.log(res)
- })
- console.log('1', promise1);
- console.log('2', promise2);
-
- 'promise1'
- '1' Promise{
: 'resolve1'} - '2' Promise{
} - 'resolve1'
return error≠throw error,所以不会被捕获错误- new Promise((resolve, reject) => {
- resolve('成功了')
- })
- .then(
- (data) => { console.log('onResolved1', data); },
- (error) => { console.log('onRejected1', error); }
- )
- .then(
- (data) => { console.log('onResolved2', data); },
- (error) => { console.log('onRejected2', error); }
- )
- > "onResolved1" "成功了"
- > "onResolved2" undefined
因为回调函数无返回值,所以resolve(x)中的x为undefined
- // MyPromise.js
-
- class MyPromise {
- ...
- then(onFulfilled, onRejected) {
- // 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
- const promise2 = new MyPromise((resolve, reject) => {
- // 这里的内容在执行器中,会立即执行
- if (this.status === FULFILLED) {
- // 获取成功回调函数的执行结果
- const x = onFulfilled(this.value);
- // 传入 resolvePromise 集中处理
- resolvePromise(x, resolve, reject);
-
- } ...
- })
-
- return promise2;
- }
- }
-
- function resolvePromise(x, resolve, reject) {
- // 判断x是不是 MyPromise 实例对象
- if(x instanceof MyPromise) {
- // 执行 x,调用 then 方法,目的是将其状态变为 fulfilled 或者 rejected
- // x.then(value => resolve(value), reason => reject(reason))
- // 简化之后
- x.then(resolve, reject)
- } else{
- // 普通值
- resolve(x)
- }
- }
- Promise.resolve().then(() => {
- console.log(0);
- return Promise.resolve(4);
- }).then((res) => {
- console.log(res)
- })
-
- Promise.resolve().then(() => {
- console.log(1);
- }).then(() => {
- console.log(2);
- }).then(() => {
- console.log(3);
- }).then(() => {
- console.log(5);
- }).then(() =>{
- console.log(6);
- })
-
- // 0123456
-
return Promise.resolve(4);
x 等于 realOnFulfilled(this.value) 的执行结果,也就是 return 出来的 MyPromise.resolve(4),所以在 x 传入 resolvePromise 方法中进行类型判断时,会发现它是一个 Promise 对象(存在 then 方法),并让其调用 then 方法完成状态转换
- // MyPromise.js
-
- // 获取成功回调函数的执行结果
- const x = realOnFulfilled(this.value);
- // 传入 resolvePromise 集中处理
- resolvePromise(promise2, x, resolve, reject);
Js引擎为了让microtask尽快的输出,做了一些优化,连续的多个then(3个)如果没有reject或者resolve会交替执行then而不至于让一个堵太久完成用户无响应,不单单v8这样其他引擎也是这样,因为其实promuse内部状态已经结束了。这块在v8源码里有完整的体现
在JavaScript中,字符串是不可变的(immutable),str是基本数据类型,或者string对象,String的方法都是返回新值
可变只有数组和对象,不可变可以带来性能和安全性的好处
- let str=new String("123")
- str[0]="z"
- console.log(str[0])//1
- let str = "Hello";
- let index = str.indexOf("",0);
- console.log(index); // 输出 0
- index = str.indexOf("",3);
- console.log(index); // 输出 3
- index = str.indexOf("",6);
- console.log(index); // 输出 5
- var arr = [];
- arr[''] = 1;
- console.log(arr); // []
-
- arr[2] = 2;
- console.log(arr); // [undefined, undefined, 2]
-
- arr.length = 0;
- console.log(arr); // []
- console.log(0.1+0.2);//0.30000000000000004
- num.toFixed(1);
编译器会从左到右一个字符一个字符解析,如果已解析的字符已经能够组成一个符号,再解析下一个字符,判断下一个字符能否和已解析出的符号再次组合成一个符号,如果能,再不断重复如上过程;如果不能,则使用最终解析出的符号。
- var n=1
- n=n++
- console.log(n)//1
-
- //等价于
- var n=1;
- //n=n++运算开始
- var temp=n; //编译器解析到“n=n++”中的“n++”后,会先在内存中缓存n的原值,用来参与其他运算
- n = n+1; //编译器解析到“n=n++”中的“=”后,会在做“=”运算前将n自加
- n = temp; //变量自加结束后,用缓存的原值进行“=”运算
- //n=n++运算结束
- console.log(n) //1
-
- var a=3,b;
- b=a++*a++;
- console.log(b) //12
-
- var a=3,b;
- b=a+++a; //b=(a++)+a, b=3+4
- console.log(b) //7