• js看代码说输出


    目录

    原型

    Function与Object

    自身找不到才会去原型链上找

    new fn()

    原型链

    constructor

    function.length

    默认参数:第一个具有默认值之前的参数个数

    剩余参数:不算进length

    闭包

    函数工厂:通过形参传递来创建其他函数的模式

    this+闭包

    this

    bind永久绑定this,apply/call也不能改变

    JS预解析/编译(变量提升)

    let

    暂时性死区

    =优先级:从右到左

    setTimeout

    原因:var变量提升到全局,可以重复声明

    解决

    A.专用:setTimeout(functionRef, delay, param...)

    B.通用

    a.立即执行函数IIFE传参

    b.let/forEach():每次迭代i是新变量,新块级作用域

    输出顺序Event loop

    async、await事件轮询执行时机

    async隐式返回Promise

    await xx;yy=promise(xx).then(yy)微任务

    await pending promise

    Node中的process.nextTick

    process.nextTick执行顺序早于微任务

    promise

    pending

    new promise 初始为pengding,不存在为null的情况

    return  error≠throw error,所以不会被捕获错误

    then链式调用

    连续3个以上的then链式调用

    string

    str[i]=赋值

    str.indexOf('',i):i

    arr

    float运算

    n=n++

    缓存原值,自增,用缓存的原值进行运算

    从左到右解析,能组成符号就组


    原型

    Function与Object

    1. var F = function() {};
    2. Object.prototype.a = function() {
    3. console.log('a');
    4. };
    5. Function.prototype.b = function() {
    6. console.log('b');
    7. }
    8. var f = new F();
    9. f.a(); // a
    10. f.b(); // f.b is not a function
    11. F.a(); // a
    12. F.b(); // b

    Function.prototype === Function.__proto__; // true

    Function本身也是函数,所以 Function是Function的实例

    注:箭头表继承

    Object⬅️F(F类的构造函数)⬅️f(f是F类实例)

    ⬆️

    Function

    ⬆️

    F(Function实例)

    由函数Function实例F创建的实例f不再是函数Function

    自身找不到才会去原型链上找

    1. var a=1;
    2. function fn1(){
    3. var a=2;
    4. console.log(this.a+a);
    5. }
    6. var fn3=function(){
    7. this.a=3;
    8. }
    9. //自身找不到才会去原型链上找,可以理解为替补
    10. fn3.prototype={
    11. a:4
    12. }
    13. var fn33=new fn3();
    14. fn1.call(fn33)
    15. //5

    new fn()

    1. function A() {}
    2. function B(a) {
    3. this.a = a;
    4. }
    5. function C(a) {
    6. if (a) {
    7. this.a = a;
    8. }
    9. }
    10. A.prototype.a = 1;
    11. B.prototype.a = 1;
    12. C.prototype.a = 1;
    13. console.log(new A().a); //1
    14. console.log(new B().a); //undefined(传入a为undefined)
    15. console.log(new C(2).a);//2
    16. 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
    
    1. function fun(){
    2. this.a = 0
    3. this.b = function(){
    4. console.log("自己的b:",this.a)
    5. }
    6. }
    7. fun.prototype = {
    8. b: function(){
    9. this.a = 20
    10. console.log("原型链b:",this.a)
    11. },
    12. c: function (){
    13. this.a = 30
    14. console.log(this.a)
    15. }
    16. }
    17. var my_fun = new fun()
    18. my_fun.b() // 0
    19. my_fun.c() // 30
    1. function Foo() {
    2. getName = function (){
    3. console.log(1)
    4. }
    5. return this
    6. }
    7. Foo.getName = function () {
    8. console.log(2)
    9. }
    10. Foo.prototype.getName = function(){
    11. console.log(3)
    12. }
    13. Foo.getName()//2
    14. Foo().getName();//1
    15. getName();//1:getName函数变量提升到全局
    16. new Foo.getName()//2 Foo函数有对象有个getName(...2)属性方法
    17. //先对 new Foo() 实例化再对 A.getName() 调用,
    18. //对 new Foo() 实例化调用的 getName() 方法是原型 prototype 上的
    19. new Foo().getName()//3
    20. // new new Foo().getName() => new B.getName(),
    21. //先对 new Foo() 实例化再对 new B.getName() 实例化,
    22. //new B.getName() 同时也在执行 B.getName() 方法输出的还是实例 B 上的方法也就是原型 prototype 的 getName
    23. new new Foo().getName()//3

    constructor

    f1,f2中本没有 constructor 但是会从构造函数的 prototype 中查找相当f1.prototype.constructorf2的原型被重新定义了指向基类 object

    找不到的,只会往上找,而非往下,所以原型上不存在n

    1. function Fn(){
    2. var n = 10
    3. this.m = 20
    4. this.aa = function() {
    5. console.log(this.m)
    6. }
    7. }
    8. Fn.prototype.bb = function () {
    9. console.log("原型的this.n",this.n)
    10. }
    11. var f1 = new Fn
    12. Fn.prototype = {
    13. aa: function(){
    14. console.log(this.m + 10)
    15. }
    16. }
    17. var f2 = new Fn
    18. //注意区别修改原型Fn.prototype和修改原型的属性Fn.prototype.bb
    19. console.log(f1.constructor) // ==> function Fn(){...}
    20. console.log(f2.constructor) // ==> Object() { [native code] }
    21. //原型中
    22. f1.bb() // n是 undefined
    23. //自己有aa方法,就不去原型链上找了
    24. f1.aa() // 20
    25. f2.aa() // 20
    26. //原型链上的aa方法中,原型没有m属性,undefined+10=NaN
    27. f2.__proto__.aa() // NaN
    28. f2.bb() // Uncaught TypeError: f2.bb is not a function

    function.length

    默认参数:第一个具有默认值之前的参数个数

    1. function fn1 (name) {}
    2. function fn2 (name = '林三心') {}
    3. function fn3 (name, age = 22) {}
    4. function fn4 (name, age = 22, gender) {}
    5. function fn5(name = '林三心', age, gender) { }
    6. console.log(fn1.length) // 1
    7. console.log(fn2.length) // 0
    8. console.log(fn3.length) // 1
    9. console.log(fn4.length) // 1
    10. console.log(fn5.length) // 0

    剩余参数:不算进length

    1. function fn1(name, ...args) {}
    2. console.log(fn1.length) // 1

    闭包

    1. var ary = [1, 2, 3, 4]
    2. function fn(i){
    3. return function(n){
    4. console.log(n+ (i++))
    5. }
    6. }
    7. var f = fn(10)
    8. f(20) // 30 (n+10)
    9. //fn(10)被f引用,未被释放
    10. f(20) // 31 (n+11)
    11. //就算都是fn(10),也只是函数体相同但地址不同的两个函数
    12. fn(10)(20) // 30
    13. //fn(10)执行完后就释放了
    14. fn(10)(20) // 30
    15. // console.log(i) // Uncaught ReferenceError: i is not defined

    函数工厂:通过形参传递来创建其他函数的模式

    1. function createMultiplier(factor) {
    2. // 返回一个新函数,这个函数将传入的参数与 factor 相乘
    3. return function (number) {
    4. return number * factor;
    5. };
    6. }
    7. // 创建一个乘以 2 的函数
    8. const double = createMultiplier(2);
    9. // 创建一个乘以 3 的函数
    10. const triple = createMultiplier(3);
    11. // 使用这些函数
    12. console.log(double(5)); // 输出 10,因为 5 * 2 = 10
    13. console.log(triple(5)); // 输出 15,因为 5 * 3 = 15

    this+闭包

    1. var num = 10 // 60; 65
    2. var obj = {
    3. num: 20
    4. }
    5. //自执行obj.fn= function(n){this.num+=n...}
    6. obj.fn = (function (num){
    7. this.num = num * 3 // 调用者是window,window.num=20*3
    8. num++ // 21
    9. return function(n){
    10. this.num += n // 60 + 5 = 65;20 + 10 =30
    11. num++ // 21 + 1 = 22;22 + 1 = 23 闭包引用num
    12. console.log(num)
    13. }
    14. })(obj.num)
    15. var fn = obj.fn
    16. fn(5) // 22 this 指向 window
    17. obj.fn(10) // 23 this 指向 obj
    18. console.log(num, obj.num) // 65, 30

    this

    1. this.count=1
    2. function func() {
    3. console.log(++this.count)
    4. }
    5. func.count = 0
    6. func()//2

    bind永久绑定this,apply/call也不能改变

    1. obj = {
    2. func() {
    3. //箭头函数继承调用func()的this
    4. const arrowFunc = () => {
    5. console.log(this._name)
    6. }
    7. return arrowFunc
    8. },
    9. _name: "obj",
    10. }
    11. //仅赋予函数体内容
    12. func = obj.func
    13. //调用者为全局,全局下没有“_name”属性
    14. func()()//undefined
    15. obj.func()()//obj
    16. //bind绑定但不调用
    17. obj.func.bind({ _name: "newObj" })()()//newObj
    18. //apply绑定并调用
    19. obj.func.apply({ _name: "newObj" })()//newObj
    20. //传入空this
    21. obj.func.bind()()()//undefined
    22. //无论这个新函数如何被调用,this 被永久绑定到 { _name: "bindObj" }
    23. obj.func.bind({ _name: "bindObj" }).apply({ _name: "applyObj" })()//bindObj


    JS预解析/编译(变量提升)

    1. //虽然按顺序创建作用域,不会报错a为声明
    2. function foo() {
    3. console.log(a);
    4. }
    5. function bar() {
    6. var a="bar"
    7. foo();
    8. }
    9. bar(); //undefined
    10. var a="window"
    1. var a = 1;
    2. function foo(a, b) {
    3. console.log(a); // 1
    4. a = 2;
    5. arguments[0] = 3;
    6. var a;
    7. console.log(a, this.a, b); // 3, 1, undefined
    8. }
    9. foo(a);

    let

    暂时性死区

    1. function test() {
    2. var foo = 33;
    3. if (foo) {// var foo
    4. //foo+55是let 的 foo
    5. let foo = foo + 55; // ReferenceError
    6. }
    7. }
    8. test();

    标识符 n.a 被解析为位于指令(let n)本身的 n 对象的属性 a。因为 n 的声明尚未执行结束,它仍然处于暂时性死区内

    1. function go(n) {
    2. // n 在此处被定义
    3. console.log(n); // { a: [1, 2, 3] }
    4. for (let n of n.a) {
    5. // ^ ReferenceError
    6. console.log(n);
    7. }
    8. }
    9. go({ a: [1, 2, 3] });

    =优先级:从右到左

    = 优先级是从右到左的,所以变量提升阶段 b=undefined后,将 c 赋值成 undefined

    1. var b = {
    2. a,
    3. c: b
    4. }
    5. console.log(b.c);
    6. //undefined

    setTimeout

    原因:var变量提升到全局,可以重复声明

    1. for(var i=0;i<2;i++){
    2. setTimeout(()=>{console.log(i)},1000)//3,3
    3. }
    4. for(var i=0;i<3;i++){
    5. setTimeout(()=>{console.log(i)},1000)//3,3,3
    6. }
    7. console.log(i)//3

    解决

    A.专用:setTimeout(functionRef, delay, param...)

    1. // 利用setTimeout的第三个参数,第三个参数将作为setTimeout第一个参数的参数
    2. for (var i = 0; i < 5; i++) {
    3. setTimeout(function fn(i) {
    4. console.log(i);
    5. }, 1000, i); // 第三个参数i,将作为fn的参数
    6. }

    B.通用

    a.立即执行函数IIFE传参
    1. //输出0,1,2,3,4
    2. //立即执行函数传参,就不会引用var i了
    3. for (var i = 0; i < 5; i++) {
    4. (function(j) {
    5. setTimeout(function timer() {
    6. console.log(j);
    7. }, 1000);
    8. })(i);
    9. //等效于
    10. setTimeout((function(i){
    11. return () => console.log(i);
    12. })(i),1000)
    13. }
    b.let/forEach():每次迭代i是新变量,新块级作用域
    1. for(let i=0;i<2;i++){
    2. setTimeout(()=>{console.log(i)},1000)//0,1
    3. }

    输出顺序Event loop

    1. //宏任务队列:[]
    2. //微任务队列:[promise0]
    3. Promise.resolve()
    4. .then(function() {
    5. console.log("promise0");
    6. })
    7. .then(function() {
    8. console.log("promise5");
    9. });
    10. //定时的setTimeout(delay=0)=setImmediate:下个Event Loop执行
    11. //宏任务队列:[timer1]
    12. //微任务队列:[promise0]
    13. setTimeout(() => {
    14. console.log("timer1");
    15. Promise.resolve().then(function() {
    16. console.log("promise2");
    17. });
    18. Promise.resolve().then(function() {
    19. console.log("promise4");
    20. });
    21. }, 0);
    22. //宏任务队列:[timer1,timer2]
    23. //微任务队列:[promise0]
    24. setTimeout(() => {
    25. console.log("timer2");
    26. Promise.resolve().then(function() {
    27. console.log("promise3");
    28. });
    29. }, 0);
    30. //宏任务队列:[timer1,timer2]
    31. //微任务队列:[promise0,promise1]
    32. Promise.resolve().then(function() {
    33. console.log("promise1");
    34. });
    35. //执行start
    36. console.log("start");
    37. //执行当前所有微任务队列:[promise0,promise1]
    38. //执行promise0时将promise5放入了微任务队列:[promise1,promise5]
    39. //接着执行微任务队列:输出promise1,promise5
    40. //当微任务队列为空,开始执行宏任务队列[timer1,timer2]队首的timer1
    41. //执行timer1时碰到了微任务promise2,放进微任务队列[promise2]
    42. //宏任务timer1执行完了,开始执行所有当前所有微任务:[promise2]
    43. //执行promise2完碰到微任务promise4,放进微任务队列:[promise4]
    44. //当前微任务队列不为空,接着执行promise4
    45. //微任务队列为空,接着执行宏任务队列队首[timer2]
    46. //执行timer2时碰到了微任务promise3,放进微任务队列[promise3]
    47. //宏任务timer2执行完了,开始执行所有当前所有微任务:[promise3]
    48. // 打印结果: start promise0 promise1 promise5 timer1 promise2 promise4 timer2 promise3

    async、await事件轮询执行时机

    async隐式返回Promise


    await xx;yy=promise(xx).then(yy)微任务

    1. //1.script start(同步)
    2. console.log("script start");
    3. async function async1() {
    4. await async2(); // await 隐式返回promise
    5. console.log("async1 end"); // 这里的执行时机:在执行微任务时执行
    6. }
    7. async function async2() {
    8. console.log("async2 end"); // 这里是同步代码
    9. }
    10. //2.async2 end(同步)
    11. //微任务队列:[async1 end]
    12. async1();
    13. //宏任务队列:[setTimeout],setTimeOut进入下一loop
    14. setTimeout(function() {
    15. console.log("setTimeout");
    16. }, 0);
    17. //3.Promise(同步)
    18. //宏任务队列:[setTimeout]
    19. //微任务队列:[async1 end,promise1]
    20. new Promise(resolve => {
    21. console.log("Promise"); // 这里是同步代码
    22. resolve();
    23. })
    24. .then(function() {
    25. console.log("promise1");
    26. })
    27. .then(function() {
    28. console.log("promise2");
    29. });
    30. //4.script end(同步)
    31. console.log("script end");
    32. //当前loop的宏任务(都是同步代码)都执行完毕
    33. //执行所有微任务[async1 end,promise1]
    34. //执行promise1完后碰到了promise2,加入微任务队列,接着执行
    35. //当前所有微任务都执行完毕,开始执行宏任务队列[setTimeout]
    36. // 打印结果: script start => async2 end => Promise => script end => async1 end => promise1 => promise2 => setTimeout

    await pending promise

    1. async function async1 () {
    2. console.log('async1 start');
    3. await new Promise(resolve => {
    4. console.log('promise1')
    5. })
    6. console.log('async1 success');
    7. return 'async1 end'
    8. }
    9. console.log('srcipt start')
    10. async1().then(res => console.log(res))
    11. console.log('srcipt end')
    12. > "srcipt start"
    13. > "async1 start"
    14. > "promise1"
    15. > "srcipt end"

    Node中的process.nextTick

    process.nextTick执行顺序早于微任务

    1. console.log("start");
    2. //定时进入下一loop,宏任务队列:[timeout]
    3. setTimeout(() => {
    4. console.log("timeout");
    5. }, 0);
    6. //微任务队列:[promise]
    7. Promise.resolve().then(() => {
    8. console.log("promise");
    9. });
    10. //process.nextTick在微任务队首
    11. //微任务队列:[nextTick,promise]
    12. process.nextTick(() => {
    13. console.log("nextTick");
    14. Promise.resolve().then(() => {
    15. console.log("promise1");
    16. });
    17. });
    18. console.log("end");
    19. // 执行结果 start end nextTick promise promise1 timeout

    promise

    pending

    1.pengding时,then()收集依赖,将成功/失败回调放入成功/失败队列

    2.触发resolve/reject,改变pending,从成功/失败队列中取出回调依次执行

    1. const promise = new Promise((resolve, reject) => {
    2. console.log(1);
    3. console.log(2);
    4. });
    5. promise.then(() => {
    6. console.log(3);
    7. });
    8. console.log(4);
    9. //124

    new promise 初始为pengding,不存在为null的情况

    1. const promise1 = new Promise((resolve, reject) => {
    2. console.log('promise1')
    3. resolve('resolve1')
    4. })
    5. const promise2 = promise1.then(res => {
    6. console.log(res)
    7. })
    8. console.log('1', promise1);
    9. console.log('2', promise2);
    10. 'promise1'
    11. '1' Promise{: 'resolve1'}
    12. '2' Promise{}
    13. 'resolve1'

    return  error≠throw error,所以不会被捕获错误

    then链式调用

    1. new Promise((resolve, reject) => {
    2. resolve('成功了')
    3. })
    4. .then(
    5. (data) => { console.log('onResolved1', data); },
    6. (error) => { console.log('onRejected1', error); }
    7. )
    8. .then(
    9. (data) => { console.log('onResolved2', data); },
    10. (error) => { console.log('onRejected2', error); }
    11. )
    12. > "onResolved1" "成功了"
    13. > "onResolved2" undefined

    因为回调函数无返回值,所以resolve(x)中的x为undefined

    1. // MyPromise.js
    2. class MyPromise {
    3. ...
    4. then(onFulfilled, onRejected) {
    5. // 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
    6. const promise2 = new MyPromise((resolve, reject) => {
    7. // 这里的内容在执行器中,会立即执行
    8. if (this.status === FULFILLED) {
    9. // 获取成功回调函数的执行结果
    10. const x = onFulfilled(this.value);
    11. // 传入 resolvePromise 集中处理
    12. resolvePromise(x, resolve, reject);
    13. } ...
    14. })
    15. return promise2;
    16. }
    17. }
    18. function resolvePromise(x, resolve, reject) {
    19. // 判断x是不是 MyPromise 实例对象
    20. if(x instanceof MyPromise) {
    21. // 执行 x,调用 then 方法,目的是将其状态变为 fulfilled 或者 rejected
    22. // x.then(value => resolve(value), reason => reject(reason))
    23. // 简化之后
    24. x.then(resolve, reject)
    25. } else{
    26. // 普通值
    27. resolve(x)
    28. }
    29. }

    连续3个以上的then链式调用

    1. Promise.resolve().then(() => {
    2. console.log(0);
    3. return Promise.resolve(4);
    4. }).then((res) => {
    5. console.log(res)
    6. })
    7. Promise.resolve().then(() => {
    8. console.log(1);
    9. }).then(() => {
    10. console.log(2);
    11. }).then(() => {
    12. console.log(3);
    13. }).then(() => {
    14. console.log(5);
    15. }).then(() =>{
    16. console.log(6);
    17. })
    18. // 0123456

    return Promise.resolve(4);

    x 等于 realOnFulfilled(this.value) 的执行结果,也就是 return 出来的 MyPromise.resolve(4),所以在 x 传入 resolvePromise 方法中进行类型判断时,会发现它是一个 Promise 对象(存在 then 方法),并让其调用 then 方法完成状态转换

    1. // MyPromise.js
    2. // 获取成功回调函数的执行结果
    3. const x = realOnFulfilled(this.value);
    4. // 传入 resolvePromise 集中处理
    5. resolvePromise(promise2, x, resolve, reject);

    Js引擎为了让microtask尽快的输出,做了一些优化,连续的多个then(3个)如果没有reject或者resolve会交替执行then而不至于让一个堵太久完成用户无响应,不单单v8这样其他引擎也是这样,因为其实promuse内部状态已经结束了。这块在v8源码里有完整的体现

    string

    str[i]=赋值

    在JavaScript中,字符串是不可变的(immutable),str是基本数据类型,或者string对象,String的方法都是返回新值

    可变只有数组和对象,不可变可以带来性能和安全性的好处

    1. let str=new String("123")
    2. str[0]="z"
    3. console.log(str[0])//1

    str.indexOf('',i):i

    1. let str = "Hello";
    2. let index = str.indexOf("",0);
    3. console.log(index); // 输出 0
    4. index = str.indexOf("",3);
    5. console.log(index); // 输出 3
    6. index = str.indexOf("",6);
    7. console.log(index); // 输出 5

    arr

    1. var arr = [];
    2. arr[''] = 1;
    3. console.log(arr); // []
    4. arr[2] = 2;
    5. console.log(arr); // [undefined, undefined, 2]
    6. arr.length = 0;
    7. console.log(arr); // []

    float运算

    1. console.log(0.1+0.2);//0.30000000000000004
    2. num.toFixed(1);

    n=n++

    缓存原值,自增,用缓存的原值进行运算

    从左到右解析,能组成符号就组

    编译器会从左到右一个字符一个字符解析,如果已解析的字符已经能够组成一个符号,再解析下一个字符,判断下一个字符能否和已解析出的符号再次组合成一个符号,如果能,再不断重复如上过程;如果不能,则使用最终解析出的符号。

    1. var n=1
    2. n=n++
    3. console.log(n)//1
    4. //等价于
    5. var n=1;
    6. //n=n++运算开始
    7. var temp=n; //编译器解析到“n=n++”中的“n++”后,会先在内存中缓存n的原值,用来参与其他运算
    8. n = n+1; //编译器解析到“n=n++”中的“=”后,会在做“=”运算前将n自加
    9. n = temp; //变量自加结束后,用缓存的原值进行“=”运算
    10. //n=n++运算结束
    11. console.log(n) //1
    12. var a=3,b;
    13. b=a++*a++;
    14. console.log(b) //12
    15. var a=3,b;
    16. b=a+++a; //b=(a++)+a, b=3+4
    17. console.log(b) //7

    参考链接:JS 经典面试题初篇(this, 闭包, 原型...)含答案 - 掘金

  • 相关阅读:
    Matlab 画图(超详细)
    csrf跨站请求伪造,csrf相关的装饰器,auth认证模块,auth_user表切换,基于django中间件设计项目功能
    21.建造者模式
    zabbix-agent主动模式下自定义监控项和监控指标
    SSE图像算法优化系列三十二:Zhang\Guo图像细化算法的C语言以及SIMD指令优化
    获得1688商品详情 API
    OpenPCDet系列 | 8.4 nuScenes数据集数据调用和数据分析
    Spring Boot获取客户端的IP地址
    基于SqlSugar的开发框架循序渐进介绍(12)-- 拆分页面模块内容为组件,实现分而治之的处理
    手写生产者消费者模型
  • 原文地址:https://blog.csdn.net/qq_28838891/article/details/132907055