《JS基础小知识》

132 阅读4分钟

一. typeof

typeof 操作符返回一个字符串,表示未经计算的操作数的类型。

1. 返回 "number"

typeof 37 === 'number';
typeof 3.14 === 'number';
typeof(42) === 'number';
typeof Math.LN2 === 'number';
typeof Infinity === 'number';
typeof NaN === 'number'; // 尽管它是 "Not-A-Number" (非数值) 的缩写
typeof Number(1) === 'number'; // Number 会尝试把参数解析成数值

2. 返回 "string"

typeof '' === 'string';
typeof 'bla' === 'string';
typeof `template literal` === 'string';
typeof '1' === 'string'; // 注意内容为数字的字符串仍是字符串
typeof (typeof 1) === 'string'; // typeof 总是返回一个字符串
typeof String(1) === 'string'; // String 将任意值转换为字符串,比 toString 更安全

3. 返回 "boolean"

typeof true === 'boolean';
typeof false === 'boolean';
typeof Boolean(1) === 'boolean'; // Boolean() 会基于参数是真值还是虚值进行转换
typeof !!(1) === 'boolean'; // 两次调用 ! (逻辑非) 操作符相当于 Boolean()

4. 返回 "undefined"

typeof undefined === 'undefined';
typeof declaredButUndefinedVariable === 'undefined';
typeof undeclaredVariable === 'undefined'; 

5. 返回 "object"

typeof {a: 1} === 'object';
typeof [1, 2, 4] === 'object';
typeof new Date() === 'object';
typeof null === 'object';  // 记住

6. 返回 "function"

typeof function() {} === 'function';
typeof class C {} === 'function'
typeof Math.sin === 'function';

7. 对new 一个构造函数 的typeof

对于除了 Function 外的所有构造函数进行 new 创建一个实例,这个实例的类型都是 'object'

typeof new Number(100) === 'object';
typeof new String('String') === 'object';

new Function() 创建的实例的类型是'function'

typeof new Function() === 'function'

二.

在JS里,如果把字符串和数字相加,JS会先把数字变成字符串,进行字符串的相连 2+'4' ,返回'24'

typeof 2+3 返回的是 'number3' 。因为typeof的优先级高于+,所以先计算typeof 2'number',然后计算;'number'+3

三. NodeList

document.querySelectorAll会返回一个与指定的选择器组匹配的文档中的元素列表 (使用深度优先的先序遍历文档的节点)。返回的对象是 NodeList 。

返回值: 一个静态 NodeList,包含一个与至少一个指定选择器匹配的元素的Element对象,或者在没有匹配的情况下为空NodeList

NodeList 对象是节点的集合,通常是由属性,如Node.childNodes 和 方法,如document.querySelectorAll 返回的。

NodeList 不是一个数组,是一个类似数组的对象(Like Array Object)。虽然 NodeList 不是一个数组,但是可以使用 forEach() 来迭代。你还可以使用 Array.from() 将其转换为数组。

四. 关于变量的作用域

例1:

1    let a = 1
2    function fn1(){
3       function fn3(){
4           let  a = 3
5           fn2()
6       }
7       let a = 2
8       return fn3
9    }
10   function fn2(){
11      console.log(a)
12   }
13   let  fn = fn1()
14   fn() 

a=1 的作用域:1-14行

13行执行了fn1,返回值是fn3,所以fn就是fn3。在执行fn1的时候,先定义了函数fn3,然后a=2,这个a=2的作用域就是2-9行。

14行执行fn3,跑到第三行,声明a=3,它的作用域是3-6行。

然后执行fn2,跑到第十行,打印a。可是fn2里没有a,他在a=1的作用域里,所以打印1

例2:

1    let a = 1
    
2    function fn1(){
3       function fn2(){
4           console.log(a)
5       }
6       function fn3(){
7           let  a = 3
8           fn2()
9       }
10      let a = 2
11      return fn3
12   }
    
13   let  fn = fn1()
14   fn()

第一行a=1的作用域:1-14行

13行执行fn1,返回fn3,所以fn就是fn3。在第二行执行fn1的时候,先定义了fn2和fn3两个函数,然后a=2的作用域:2-12行。

执行fn3,跑到第六行。a=3的作用域:6-9行。

然后执行fn2,跑到第三行,打印a。fn2里没有a。fn2在a=2的作用域里,所以打印2

五. 函数调用传参

传的参数分为基本数据类型和对象类型

function inc(n){
  n++
}
let n = 10
inc(n)
console.log(n)  //10

n=10,传进函数里,由于在内存图里基本数据类型存的是值,即n 里存的就是10.所以把n 里存的东西传进去,也就是把10这个值传进去了。在函数里把10+1.但是在外边调用完函数后打印n,n的值还是10。并不会改变n的值。

function inc(arr) {
  arr.forEach((v, i) => arr[i]++)
}
let arr = [1, 2, 3]
inc(arr)
console.log(arr)  // [2,3,4]

arr是一个数组,在内存图里arr这个名字里存的是一个地址,这个地址指向[1,2,3]。把arr当作实参传进一个函数里时,传的是地址,所以在函数里对arr里的每一项+1,会改变arr这个数组