【js】你忽略的js知识

一、变量和函数同名

1、例题如下

// 下面代码的几种情况的输出是什么?
// 1、情况一:
var a = 1;
function a() {}

console.log(a); // 1


// 2、情况二:
var a = 1;
var a = function(){}

console.log(a); // Function

// 3、情况三:
var a = 1;
(function a() {
    // 'use strict'
    a = 2;// 无效赋值
    console.log(a); // Function
})();

2、知识点:

  • 因为函数名称和变量名称相同,如果函数是字面量声明那么a将会是函数,如果是定义声明a是变量

  • 函数a自调用将a变量赋值成该函数,且只读,不可改变,所以打印前a存储的是函数

二、变量的隐式转换

1、例题如下

// 下面打印的内容是什么?
var arr = [0];
if (arr) {// 1
    console.log('1=>', arr == true);// 2
} else {
    console.log('2=>', a);
};

// 结果: 1=> false




// 执行解析
    // 1、在【1】的时候实际上进行了Boolean([0])的操作
    // 2、在【2】的时候进行了变量的隐式转换  
    // [0] => [0].valueof() => [0].toString() 即 [0].join() => '0' = 0

2、知识点:

  • 一边是布尔值转换成数值进行比较

  • 一边是字符串转换成数值进行比较

  • 一边是对象,将对象转成原始值

    • valueof()

    • toString()

    • TypeError异常

  • null == undefined 是 true

  • NaN和任何值比较都是false

三、对象的属性是string或者Symbol

1、例题如下

// 分别查看下面的输出
// 知识点: 对象的属性是string或者Symbol
var a ={}, b = '123', c = 123;
a[b] = 'b'; // a[b] => a['123'] = 'b'
a[c] = 'c'; // a[c] => a['123'] = 'c'
console.log('片段一:', a[b]); // c


var a ={}, b = Symbol('123'), c = Symbol('123');
a[b] = 'b';
a[c] = 'c'; 
// Symbol('123') !== Symbol('123')
console.log('片段一:', a[b]); // b

var a ={}, b = {key: '123'}, c = {key: 123};
a[b] = 'b';// a[b] => a[b.toString()]=>a['[object Object]'] = 'b
a[c] = 'c'; // a[c] => a[c.toString()]=>a['[object Object]'] = 'c'
console.log('片段一:', a[b]); // c

四、setTimeout第三个参数

1、例题如下

// 观察输出
for(var i = 0; i < 3; i++) {
    setTimeout(_ => {
        console.log(i) // 2 2 2
    }, 0)
}

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


for(let i = 0; i < 3; i++) {
    setTimeout(_ => {
        console.log(i) // 0 1 2
    }, 0)
}


// 使用第二个参数进行前置处理
setTimeout(_ => {
    console.log('after...')
}, 1000, setTimeout(() => {
    console.log('before...')
}, 0))

五、循环并行与串行

1、例题如下

// 观察下面代码的输出
let getOut = function(n) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(n)
    }, 1000)
    })
};

let testArr1 = [1, 2, 3];
// testArr1.forEach(async (num, i) => {
//     let res = await getOut(num);
//     console.log(res);// 将会隔一秒同时输出 1 2 3
// });

// 上面代码的本质是这样:
// (async function(num) {
//     let res = await getOut(num);
//     console.log(res);
// })(num) // num = 1
// (async function(num) {
//     let res = await getOut(num);
//     console.log(res);
// })(num) // num = 2
// (async function(num) {
//     let res = await getOut(num);
//     console.log(res);
// })(num) // num = 3

2、知识点:

  • foreach进行的循环,每一个循环都是独立的作用域,是并行的,是互不影响的

  • 将循环变成串行,要么变成一个作用域的同步形式(for循环),要么使用promise的then进行串行

3、串行改写

改成每隔一秒依次输出 1 2 3

  1. for循环变成一个作用域下的同步代码

    // 1.for循环变成一个作用域下(不能let)的同步代码
    async function outputFn1() {
        for(var i = 0; i < testArr1.length; i++) {
            let res = await getOut(testArr1[i]);
            console.log(res)
        }
    }
    
    // outputFn1();
    
    async function outputFn2() {
        for(item of testArr1) {
            let res = await getOut(item);
            console.log(res)
        }
    }
    // outputFn2();

  2. 通过promise.then来调用实现串行

    // 写法一
    function outputFn3(arr) {
        let count = 0;
        function inFn() {
            if (count >= testArr1.length) return;
            let pro = Promise.resolve();
            pro.then(async res => {
                let result = await getOut(arr[count]);
                console.log(result);
                count++;
                inFn();
            })
        }
        inFn()
    }
    
    outputFn3(testArr1)
    
    // 写法二
    async function test4(x) {
        var promise= Promise.resolve();
        console.log('x=>', x);
        if (x >= list.length) return;
        promise.then(async _ => {
            const res = await square(x);
            console.log(res);
            x++;
            test4(x);
        });
    }
    test4(0);

六、箭头函数和普通函数this指向

1、例题如下

var name = 'window';
var person1 = {
    name: 'person1',
    show1: function () { 
        console.log(this.name);
    },
    show2: () => console.log(this.name),
    show3: function() {
        return function() {
            console.log(this);
        }
    },
    show4: function() {
        return () => console.log(this.name);
    }
}

var person2 = { name: 'person2' };

// 知识点
// 1、箭头函数指向是在编译期间创建,在定义那一刻就已经确定,是上下文的this,不可以改变
// 2、普通定义的函数中this是在执行时候确定,可以改变this的指向

// this => person1
person1.show1();// 'person1'
// this => person1 => person2
person1.show1.call(person2);// 'person2'


// this => window
person1.show2();// 'window'
// 箭头函数this指向无法改变
person1.show2.call(person2);// ‘window’


// var fn = person1.show3() => this => window
person1.show3()();// window对象
// var fn = person1.show3() => this => person2
person1.show3().call(person2);// person2对象


// this => person1
person1.show4()(); // 'person1'
// var fn = person.show4() => person1
person1.show4().call(person2);// 'person1'
// this => person2
person1.show4.call(person2)();// 'person2'

2、知识点

  • 箭头函数指向是在编译期间创建,在定义那一刻就已经确定,是上下文的this,不可以改变

  • 普通定义的函数中this是在执行时候确定,可以改变this的指向

七、函数科里化

1、例题如下

// 1、请实现下面功能的函数
// curring(sumFn)(1)(2)(3)(); // 6
// curring(sumFn)(1, 2)(3, 4)(5)(); // 15
// curring(sumFn)(1,)(2, 3, 4, 5)(6)(); // 21

const sumFn = function(ags) {
    // console.log(ags)
    return ags.reduce((pre, cur) => { 
        // console.log(pre + cur);
        return pre + cur;
    }, 0)
}
function curring(fn) {
    let argArr = [];
    return function inFn(...args) {
        if (args.length > 0) {
            argArr = argArr.concat(args);
            return inFn;
        } else {
            return fn(argArr);
        };
    }
};

console.log(curring(sumFn)(1)(2)(3)());
console.log(curring(sumFn)(1, 2)(3, 4)(5)());

/* 实现以下需求的函数
 * const foo = function(...ags) {}
 * 
 * foo(1, 2, 3) === 6 // true
 * foo(1)(2, 3) === 6 // true
 * foo(1)(2)(3)(4) === 10 // true 
 */

function foo(...ags) {
    let sum = ags.reduce((pre, nxt) => pre + nxt, 0);
    function fn(...args) {
        sum += args.reduce((pre, nxt) => pre + nxt, 0);
        return fn;
    }
    fn.toString = function () {
        return Number(sum);
    }
    return fn;
};

let res2 = foo(1)(2, 3);
let res1 = foo(1, 2, 3);
let res3 = foo(1)(2)(3)(4)
console.log('res2:', res2 == 6);
console.log('res1:', res1 == 6);
console.log('res3:', res3 == 10);

2、知识点

  • 柯里化通常也被称为部分求值,其含义是给函数分布传递参数

  • 每次传递参数进行处理,并返回一个更具体 的函数接受剩余的参数

  • 这中间可以嵌套多层这样的接受部分参数函数,直至返回最后的结果

八、数组、字符串的includes方法和indexof

1、例题如下

// 1、NaN includes能匹配数组中的NaN,indexof不能匹配到
let ar1 = [NaN, 1, '3'];
console.log('ar1:', ar1.includes(NaN))// true
console.log('ar1:', ar1.indexOf(NaN))// -1
// undefined includes能匹配到稀疏数组中的undefined,indexof不行
let ar2 = [];
ar2[2] = 1;
console.log('ar2:', ar2.includes(undefined));// true
console.log('ar2:', ar2.indexOf(undefined));// -1

// 2字符串和数组中的indexof方法比较
// 不同点1:字符串会进行类型转换 
let arr = ['1', '2', '3', '4'];
console.log('数组中:', arr.includes(1)) // false
console.log('数组中:', arr.includes('1')) // true

let str = '12345';
console.log('str:', str.includes(1)); // true
console.log('str:', str.indexOf(1)); // 0
console.log('str:', str.indexOf('1')); // 0

2、知识点

  • NaN includes能匹配数组中的NaN,indexof不能匹配到

  • undefined includes能匹配到稀疏数组中的undefined,indexof不行

  • 字符串和数组中的indexof方法比较

    • 字符串会进行类型转换,数组不会

八、Object.keys() 方法

1、例题如下

// 1 查看下面代码的输出是
const obj1 = {
    'a': 'aa',
    'b': 'bb',
    'c': 'cc'
}
Object.keys(obj1);
// ['a', 'b', 'c']


// 2 查看下面代码输出
const obj2 = Object.create({}, {
    getFoo: {
        value: () => this.foo,
        enumerable: false
    }
});
obj2.foo = 2;
console.log('第二:', Object.keys(obj2))// ['foo']



// 3、继承的属性也可以枚举出来
class Parent {
    constructor() {
        this.name = 'parent';
    }
}
class Child extends Parent {
    constructor() {
        super();
        this.age = 20;
    }
}
let instance = new Child();
console.log('继承的属性:', Object.keys(instance));  // ['name', 'age']


// 4、请问下面打印的顺序是什么
const sa = Symbol('a');
const obj3 = {
    5: '5',
    a: 'a',
    1: '1',
    c: 'c',
    3: '3',
    b: 'b',
}
obj3[sa] = 'sa';
console.log('顺序:', Object.keys(obj3))// ['1', '3', '5', 'a', 'c', 'b']

console.log(Object.keys(123))// []

console.log(Object.keys('123'))//   ['0', '1', '2']

2、知识点

  • 继承的属性也可以枚举出来

  • Number:大于0的整数,进行排序返回,String: 按照定义的顺序返回,Symbol: 过滤掉,不反回

  • Object.keys(null) && Object.keys(undefined) 报错

  • Object.keys(123) => Object.keys(Number(123))    Object.prototype.toString.call(Number(123)) => '[object Number]'

  • Object.keys('123') => Object.keys(String('123'))      Object.prototype.toString.call(String('123')) => '[object String]'

猜你喜欢

转载自blog.csdn.net/qq_48896417/article/details/128904665
今日推荐