JS - Object 类型 & 操作符

128 阅读5分钟

ECMAScript 中的对象是一组数据和功能的集合,对象通过 new 操作符跟一个构造函数(类名)创建 或者 直接使用 对象字面量的方式创建。Object 是所有对象的基类,所有任何对象都有基类上的方法和属性。

每个 Object 实例都有如下属性和方法

  • constructor 用于创建当前对象的函数
// Object 就是一个构造函数
const obj = new Object();

// 当构造函数不需要参数的时候,可以不要括号(不推荐)
const obj2 = new Object
  • hasOwnProperty(key: string | number | symbol) 用于判断当前对象实例(不是原型)上是否存在给定的属性
  • isPrototypeOf(obj) 用于判断当前对象是否为另一个对象的原型
  • propertyIsEnumerable(key) 判断给定的属性是否可以使用 for-in 语句枚举,即判断实例的 key 是否是可枚举的
  • toLocaleString() 返回对象的字符串表示,该字符串反映度对象所在的本地化执行环境
  • toString() 返回对象的字符串表示
  • valueOf() 返回对象对应的字符串、数值或布尔值表

自增自减操作符

  • num-- 先使用再自减
  • num++ 先使用再自增
  • --num 先自减再使用
  • ++num 先自增再使用

数值操作符 +、-,遵循如下规则(最终都会变成 number 类型

  • 对于字符串,经过如上四个操作符,字符串类型都会变成 number 类型
    • 如果是有效的数值字符串,会转化为数值再应用改变。
    • 如果字符串不是有效的数值类型,会转化为 NaN
// 有效的数值字符串
let validateNumStr = "123";
log(typeof validateNumStr); // log: string
log(typeof ++validateNumStr); // log: number
log(validateNumStr); // log: 124

// 无效的数值字符串
let invalidateNumStr = "abc";
log(typeof invalidateNumStr); // log: string
log(typeof ++invalidateNumStr); // log: number
log(invalidateNumStr); // log: NaN
  • 对于布尔值
    • 如果是 false,变为 0 再应用
    • 如果是 true,变为 1 再应用
let trueValue = true;
log(typeof trueValue); // log: boolean
log(typeof ++trueValue); // log: number
log(trueValue); // log: 2

let falseValue = false;
log(typeof falseValue); // log: boolean
log(typeof ++falseValue); // log: number
log(falseValue); // log: 1
  • 对于数值,加 1 或者减 1
  • 对于对象,先调用其 valueOf() 方法获取可操作的值
let obj = {
  valueOf() {
    return "invalidStrVal";
  },
  toString() {
    return 3;
  },
};

log(typeof obj); // log: object
log(typeof ++obj); // log: number
log(obj); // log: NaN

位操作符

位操作符用于数据的底层操作,也就是内存中表示的数据的比特(位),因为 ECMAScript 所有数值都是以 IEEE754 64 位格式存储,但是位操作并不直接应用到 64 位(不可见),它会先把 64 位转化为 32 位,在进行位操作。有符号整数使用 32 为的前 31 位表示整数值,第 32 位 0 表示正,1 表示负号。

注意⚠️:在转化为的时候,NaN 和 Infinity 在位操作中都会被当成 0 处理

负值 (补码 二进制编码存储)

一个负数的表示经过如下步骤

  1. 确定绝对值的二进制表示
  2. 找到数值的补码或反码(二进制中 0 变 1, 1 变 0)
  3. 将第二步 的结果 + 1
// 在 js 中,负值输出为一个二进制字符串时,会得到一个前面 加了 减号 的绝对值
log((-18).toString(2)); // log: -10010

按位非 ~

按位非的作用是对数值去反并减1 即 result = ~num = -num - 1; 尽管看起来差不多,但是位操作的速度快得多

按位与 &

两个操作数按照位数对其,都为 1 & 的结果为 1,其他情况为 0

比如: 0110 & 0101 = 0100 最终结果为 4 ,即 6 & 5 = 4;

const v1 = 6, v2 = 5;
log(6 & 5); // log: 4  0110 & 0101 = 0100 = 4(十进制)

按位或 |

都为 0 的时候才为0,有 1 就为 1;

按位 异或

不同的时候才为 1, 相同的时候为 0 (比如 1 & 1 = 0, 0 & 0 = 0, 1 & 0 = 1, 0 & 1 = 1)

左移 <<

会按照指定的位数将数值所有位向左移动(向左移动的数值为多少,就乘以2的多少次方)左移会用 0 来填充右边。

注意⚠️: 左移会保留所操作数的符号。比如, -2 左移 5 位,得到的结果是 -64

const old2 = 2;
const newVal = old2 << 5; // 2 * (Math.pow(2, 5), old2 没有变化

log(old2.toString(2)); // 0000010 (2 的二进制)
log(newVal); // log: 64
log(newVal.toString(2)); // log: 1000000 (64 的二进制)

有符号右移 >>

右移符号位不会变化,只是符号位之后的位码在移动,即会保留符号(正或负)

const oldValue = 64; // 二进制位: 1000000
log(oldValue >> 5); // 右移 5 位 后,二进制变为: 10,即十进制 2

无符号右移 >>>

无符号右移会将数值的所有 32 位都向右移,即不论正负,符号位都参与右移

const negativeVal = -64;
log(-64 >>> 5); // log: 134217726, 这就是无符号右移,变化太大了

const positiveVal = 64;
log(64 >>> 5); // log: 2, 正数的无符号右移和有符号右移一样

指数操作符 **

ECMAScript 7 新增的指数操作符 **, 等效于 Math.pow(x,y) 即 x 的 y 次幂

log(Math.pow(2, 3)); // 8;

log(2 ** 3); // 8

逻辑非 !

这个操作符始终会返回布尔值,只有 空字符串'', 0, null, NaN, undefined 五个值会返回 true,其他都会变为 false

两个 !! 相当于使用 Boolean(val) 来讲 val 转换为 boolan 值

// 记住吧,只有这个 6个 经过非之后才是 true,其他的值 !都是 false
log(!""); // true,
log(!NaN); // true;
log(!undefined); // true;
log(!0); // true;
log(!null); // true
log(!false); // true

log(!!""); // false,
log(!!NaN); // false;
log(!!undefined); // false;
log(!!0); // false;
log(!!null); // false
log(!!false); // false

逻辑与 &&

逻辑与 && 是一种短路操作符,即如果第一个操作数决定了结果为 false,那么永远不会对第二个操作数求值;只有两个为 真,其与的结果才为真

逻辑或 ||

如果有一个为真,其或的结果就为真;如果第一个操作数 为真,则直接短路,不会计算第二个值,直接返回为真

乘法操作符 *

const res = num1 * num2

计算规则如下:

  • 如果两个正常的数值相乘,如果 ECMAScript 不能表示乘积,则返回 Infinity 或 -Infinity
  • 如果有任意操作数是 NaN, 则返回 NaN
  • 如果 Infinity * 0 , 返回 NaN
  • 如果 Infinity * 非 0 有限数值,则根据 第二个操作数的符号返回 Infinity 或 -Infinity
  • 如果 Infinity * Infinity, 则返回 Infinity
  • 如果不是数值的操作数,则先在后台用 Number() 将其转化为数值,然后再应用上数规则

加法操作数 +

注意⚠️: 如果 操作数中有一个是字符串,则会以 字符串拼接的方式将两个操作数组合在一起

比较操作符 >, <, >=, <=, ==, ===

  • 只要是设计 NaN 的比较,除了 NaN != NaN 和 NaN !== NaN 外最终结果都会返回 false
log(NaN == NaN); // log: false
log(NaN === NaN); // log: false
log(NaN >= NaN); // log: false
log(NaN <= NaN); // log: false
log(NaN > NaN); // log: false
log(NaN < NaN); // log: false

log(NaN != NaN); // log: true
log(NaN !== NaN); // log: true

等于操作符 ==

  • 如果存在一操作数为 boolean 类型或者 number 类型,会优先将 其转化为 number 类型比较
  • 如果一操作数是对象,而另外一个操作数不是对象,则调用对象的 valueOf() 方法获取其原始值,然后在进行比较
  • null 和 undefined 相等。
  • null 和 undefined 不再进行转化为其他类型的值进行比较
  • 如果 存在 NaN,则返回 false, 不相等返回 true ( NaN 不等于 NaN)
  • 如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回true。否则,两者不相等。

全等(=== ) 和不全等(!==) 操作符

全等和不全等操作符与相等和不相等操作符类似,只不过它们在比较相等时不转换操作数。全等操作符(===)和不全等操作符(!==)两个在进行比较的时候不会进行转化

log(55 == "55"); // log:true; 在等于的时候会进行「转化」后比较
log(55 === "55"); // log:false; 全等于的情况「不进行转化」比较

log(55 != "55"); // log: false; 在不等于的时候会进行「转化」后比较
log(55 !== "55"); // log: true; 不全等于(或严格不等于)的情况「不进行转化」比较

赋值操作符

  • result = express; 将右边的 express 赋值给 result
  • result *= express; 乘后赋值
  • result /= express; 除后赋值
  • result %= express; 取模后赋值
  • result += express; 加后赋值
  • result -= express; 减后赋值
  • result <<= express; 左移后赋值
  • result >>= express; 右移后赋值
  • result >>>= express; 无符号右移后赋值