Skip to content

类型和语法

JS 数据类型

基本类型: Number、Boolean、String、Null、Undefined、Symbol、BigInt

引用类型: Object

typeof

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

变量未持有值时为 undefined ,此时 typeof 返回 undefined.

js
typeof undefined === "undefined"; // true
typeof true === "boolean"; // true
typeof 42 === "number"; // true
typeof "42" === "string"; // true
typeof { life: 42 } === "object"; // true
typeof Symbol() === "symbol"; // true
typeof null === "object"; // true
typeof function a() {} === "function"; // true
typeof NaN === "number"; // true

instanceof

instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。基本类型不能使用 instanceof 运算符。

js
function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}
const auto = new Car("Honda", "Accord", 1998);
console.log(auto instanceof Car); // true
console.log(auto instanceof Object); // true

let date = new Date();
date instanceof Date; // true
date instanceof Object; // true
date instanceof Array; // false

Object.prototype.toString.call()

Object.prototype.toString.call() 方法:这是一种更准确的类型判断方法,可以判断出更多类型的数据,但是使用起来不太方便,需要借助 call 或者 apply 来调用。

js
Object.prototype.toString.call(2); // "[object Number]"
Object.prototype.toString.call("2"); // "[object String]"
Object.prototype.toString.call(true); // "[object Boolean]"
Object.prototype.toString.call(undefined); // "[object Undefined]"
Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call({}); // "[object Object]"
Object.prototype.toString.call([]); // "[object Array]"
Object.prototype.toString.call(function () {}); // "[object Function]"
Object.prototype.toString.call(new Date()); // "[object Date]"
Object.prototype.toString.call(Math); // "[object Math]"
Object.prototype.toString.call(/abc/); // "[object RegExp]"
Object.prototype.toString.call(new Error()); // "[object Error]"

Array.isArray()

Array.isArray() 方法:用于确定传递的值是否是一个 Array。

js
Array.isArray([1, 2, 3]); // true
Array.isArray({ foo: 123 }); // false
Array.isArray("foobar"); // false
Array.isArray(undefined); // false

constructor

constructor 属性:返回对创建此对象的数组函数的引用。

js
let arr = [1, 2, 3];
arr.constructor === Array; // true

let num = 1;
num.constructor === Number; // true

let str = "Hello";
str.constructor === String; // true

Null & Undefined

undefined 类型只有一个值,即 undefined。null 类型也只有一个值,即 null。 它们的名称既是类型也是值。undefined 和 null 常被用来表示“空的”值或“不是值”的值。二者之间有一些细微的差别。

例如:

• null 指空值(empty value)

• undefined 指没有值(missing value)

或者:

• undefined 指从未赋值

• null 指曾赋过值,但是目前没有值

null 是一个特殊关键字,不是标识符,我们不能将其当作变量来使用和赋值。

然而 undefined 却是一个标识符,可以被当作变量来使用和赋值。

NaN

NaN 非自反 NaN !== NaN => true

全局工具函数 isNaN(...) 判断一个值是否是 NaN. (不建议使用),从 ES6 开始我们可以使用工具函数 Number.isNaN(..)。

js
isNaN(a); //true
isNaN("foo"); //true
Number.isNaN(a); //true
Number.isNaN("foo"); //false

Infinity / Infinity; //  NaN

我们应该尽量使用 Number.isNaN(..)这样可靠的方法

原生函数

原生函数可以被当作构造函数来使用. 例如: new Array(1, 2, 3) 会构造出 [1, 2, 3] 数组.

原生函数有很多种, 例如:

  • String()
  • Number()
  • Boolean()
  • Array()
  • Object()
  • Function()
  • RegExp()
  • Date()
  • Error()
  • Symbol()

类型转换

类型转换

问:symbol 有什么用处

可以用来表示一个独一无二的变量防止命名冲突。

可以利用 symbol 不会被常规的方法(除了 Object.getOwnPropertySymbols 外)遍历到,所以可以用来模拟私有变量。

主要用来提供遍历接口,布置了 symbol.iterator 的对象才可以使用 for···of 循环,可以统一处理数据结构。调用之后回返回一个遍历器对象,包含有一个 next 方法,使用 next 方法后有两个返回值 value 和 done 分别表示函数当前执行位置的值和是否遍历完毕。

Symbol.for() 可以在全局访问 symbol 变量,Symbol.keyFor() 可以获取到全局 symbol 变量的 key。

问:new 一个构造函数,如果函数返回 return {}return nullreturn 1return true 会发生什么情况?

如果函数返回一个对象,那么 new 这个函数调用返回这个函数的返回对象,否则返回 new 创建的新对象

(3)箭头函数和普通函数有啥区别?箭头函数能当构造函数吗?

  • 普通函数通过 function 关键字定义, this 无法结合词法作用域使用,在运行时绑定,只取决于函数的调用方式,在哪里被调用,调用位置。(取决于调用者,和是否独立运行)

  • 箭头函数使用被称为 “胖箭头” 的操作 => 定义,箭头函数不应用普通函数 this 绑定的四种规则,而是根据外层(函数或全局)的作用域来决定 this,且箭头函数的绑定无法被修改(new 也不行)。

    • 一个函数内部有两个方法:[[Call]] 和 [[Construct]],在通过 new 进行函数调用时,会执行 [[construct]] 方法,创建一个实例对象,然后再执行这个函数体,将函数的 this 绑定在这个实例对象上
  • 当直接调用时,执行 [[Call]] 方法,直接执行函数体

  • 箭头函数没有 [[Construct]] 方法,不能被用作构造函数调用,当使用 new 进行函数调用时会报错。

  • 箭头函数常用于回调函数中,包括事件处理器或定时器

  • 箭头函数和 var self = this,都试图取代传统的 this 运行机制,将 this 的绑定拉回到词法作用域

  • 没有原型、没有 this、没有 super,没有 arguments,没有 new.target

  • 不能通过 new 关键字调用

function foo() {
  return (a) => {
    console.log(this.a);
  }
}

var obj1 = {
  a: 2
}

var obj2 = {
  a: 3
}

var bar = foo.call(obj1);
bar.call(obj2);

参考资料

isNaN 和 Number.isNaN 函数的区别?

var、let、const

  • var 声明的范围是函数作用域,let 声明的范围是块作用域
  • var 存在变量提升
  • var 允许重复声明
  • let 有暂时性死区
  • var 全局作用域中声明的变量会成为 window 对象属性
  • let 不能够条件声明
  • const 声明必须初始化,不能够修改

Array 常见方法

方法方法方法方法方法
concat()every()fill()filter()find()
findIndex()flat()flatMap()forEach()Array.from()
Array.of()includes()indexOf()isArray()join()
keys()lastIndexOf()map()pop()push()
reduce()reduceRight()reverse()shift()slice()
some()sort()splice()toLocaleString()toString()
unshift()values()copyWithin()lastIndexOf()fill()

Array.from() & Array.of()

Array.from()Array.of() 是 JavaScript 中的两个数组方法,它们的主要区别在于它们的用途和参数。

Array.from() 方法从类数组对象或可迭代对象创建一个新的数组实例。类数组对象包括具有 length 属性和可索引元素的对象,或者可迭代对象如 Set 和 Map。例如:

javascript
let set = new Set(["a", "b", "c"]);
let arrFromSet = Array.from(set);
console.log(arrFromSet); // Output: ['a', 'b', 'c']

Array.of() 方法创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型。例如:

javascript
let arrOfNumbers = Array.of(1, 2, 3);
console.log(arrOfNumbers); // Output: [1, 2, 3]

所以,Array.from() 主要用于将类数组对象或可迭代对象转换为数组,而 Array.of() 主要用于创建一个新的数组实例。

other

使用 delete 运算符可以将单元从数组中删除,但是请注意,单元删除后,数组的 length 属性并不会发生变化。删除的位置变为 <1 empty item>

js
let nums = [];
nums[0] = 1;
nums[2] = 3;
console.log(nums[1]); //undefined

其中的“空白单元”(empty slot)可能会导致出人意料的结果。a[1]的值为 undefined,但这与将其显式赋值为 undefined(a[1] =undefined)还是有所区别。

如果字符串键值能够被强制类型转换为十进制数字的话,它就会被当作数字索引来处理。a['13']=2; a.length => 14

类数组 => 数组

js
Array.from(arguments);

Array.prototype.slice.call(arguments);

String 常见方法

方法方法方法方法
charAt()charCodeAt()codePointAt()concat()
String.fromCharCode()String.fromCodePoint()includes()indexOf()
localeCompare()match()matchAll()normalize()
padStart()String.raw()repeat()replace()
search()slice()split()startsWith()
toLocaleLowerCase()toLocaleUpperCase()toLowerCase()toString()
trim()trimEnd()trimStart()valueOf()
endsWith()endsWith()lastIndexOf()padEnd()
padEnd()replaceAll()substring()toUpperCase()

Object.defineProperty()

要注意有一个小小的例外:即便属性是 configurable:false,我们还是可以把 writable 的状态由 true 改为 false,但是无法由 false 改为 true。

configurable:false 会禁止删除这个属性

writable:false,configurable:false 可以创建一个常量属性。

如果你想禁止一个对象添加新属性并且保留已有属性,可以使用 Object.preventExtensions(obj)

Object.seal(..)

会创建一个“密封”的对象,这个方法实际上会在一个现有对象上调用 Object.preventExtensions(..)并把所有现有属性标记为 configurable:false。(不能添加新属性,不能重新配置或删除属性,但可以修改属性值

Object.freeze(..)

会创建一个冻结对象,这个方法实际上会在一个现有对象上调用 Object.seal(..)并把所有“数据访问”属性标记为 writable:false,这样就无法修改它们的值。(浅层)

obj.hasOwnProperty("ab")判断对象是否存在这个属性。(只检查当前对像)

in 操作符

注意:看起来 in 操作符可以检查容器内是否有某个值,但是它实际上检查的是某个属性名是否存在。对于数组来说这个区别非常重要,4 in [2, 4, 6]的结果并不是你期待的 True,因为[2, 4, 6]这个数组中包含的属性名是 0、1、2,没有 4。

obj.propertyIsEnumerable("ab")

会检查给定的属性名是否直接存在于对象中(而不是在原型链上)并且满足 enumerable:true。

Object.keys(..)

返回一个数组,包含所有可枚举属性

Object.getOwnPropertyNames(..)

返回一个数组,包含所有属性,无论它们是否可枚举。

对象不可以使用 for...of 遍历,但是可以自定义迭代器实现

问:如何判断一个对象是不是空对象?

Released under the MIT License.