JS相关知识总结(一)

总结下这段时间吸收的许多小知识,以备忘记后翻阅。

关于面向对象

面向对象特征:

  • 具有唯一标识性
  • 具有状态
  • 具有行为

JS的面向对象和JAVA的实现思路不一样,JS是基于原型并非基于类。但是JS为了看起来更像JAVA,为此添加了一些特性。

抛开为了看起来像JAVA引入的特性,JS面向对象总结起来很简单:每个对象都有原型,对象上找不到属性就去自身原型上找,直到Object.prototype。

在ES3时代为了操作原型我们没有办法只能使用 new但是ES5之后引入了专门操作原型的方法:

  • Object.create()
  • Object.setPrototypeOf()
  • Object.getPrototypeOf()

这样我们就可以抛开类的概念直接面对原型来实现面向对象。

JS关于模拟JAVA的面向对象部分

在我的理解中有以下部分属于模拟JAVA的行为:

  • function 可以被new调用
  • 引入this机制
  • instanceof 操作符

通过 new 调用函数主要做了三件事:

  • 使用被new 调用的函数的prototype属性构造一个新对象
  • 将新对象作为函数的this,调用该函数
  • 如没有引用类型返回则返回这个新建的对象

上面就是在模拟JAVA中的类,在ES5中抛开new我们可以做到同样的事情,并且不会有让人以为这是一个类。

this更是复杂,一大批文章去解释JS中的this,由此也可以看出有些复杂。

JS中的数据类型

JS一共有7中数据类型:

  • Null
  • Undefined
  • Number
  • String
  • Boolean
  • Symbol (ES6新加)
  • Object (引用类型)

7种数据类型又被分为两类:基本类型和引用类型。

怎么判断数据对应的是哪种数据类型呢?

总结有三种:typeofinstanceofObject.prototype.toString.call(),这三种都有自己的特点。

typeof

基本上可以直接返回数据对应的类型,有两个例外,一个是 typeof null返回 "object",另一个是 typeof function() {}返回 "function"

instanceof

左边是一个对象右边是一个构造函数。

instanceof会遍历对象的原型,查找是否有函数的prototype属性。

我认为,检查数据是否是数组就很方便了 [] instanceof Array,当然这个是有问题的,所以后面又提供了Array.isArray()方法判断是否是数组。

Object.prototype.toString.call()

该方法提供的返回值十分详细,不仅会返回上面其中类型,还包括很多JS内置对象,例如:ArrayDateRegExp等。

但是这个对象有个问题,会强制装箱。也就是说判断不出是基本类型还是引用类型,如果在意的话需要配合上typeof


Object.prototype.toString.call(1); // [object Number]

Object.prototype.toString.call(new Number(1)); // [object Number]

返回值都一样。

关于装箱和拆箱

说道装箱和拆箱就要提及一下它的表象 – 隐式类型转换。

JS中隐式类型转换应用广泛,最臭名昭著的就是 ==。还有当我们直接调用 str.indexOf方法的时候,str明明是基本值还不报错就是装箱在起作用。

装箱

每个基本值都有一个对应的对象类,装箱就是将基本值转换为对应的对象类型。但是Symbol是不能通过new 调用的,所以我们要另想办法。

上面提到的Object.prototype.toString.call会强制装箱:

function a() {
  return this;
}
a.call(Symbol())  // 查看打印

拆箱(toPrimitive)

拆箱会先调用对象的valueOf方法,如果返回值是非基本值再调用toString方法,返回值非基本值则报错。

还记得偏门的面试题么?

	if (a == 2 && a == 3) {
      console.log(true);
	}

虽然是语言不好的一面,但也可以了解下。这里就用到了拆箱知识。

如果 a 是对象和基本类型做 ==比较首先会对 a 拆箱,拆箱步骤如上所述。

let b = 1;
let a = {
  valueOf: () => ++b
}

if (a == 2 && a == 3) {
  console.log(true);
}

这样就会打印出 true

自定义拆箱

关于拆箱最后想说的是现在可以自定义这个行为了。

let o = {
  [Symbol.toPrimitive]: () => "hello world"
};

console.log('' + o);

Number

关于Number一直有一个疑问就是NaN到底是不是数字。typeof NaN其实返回的是 number 。所以Not a Number 是一个 Number。

我们都知道JS中数字有精度问题:0.1 + 0.2 === 0.3返回的是false。这个时候们就要借助 Number.EPSILON来帮我们判断,Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON

JS中是区分 +0 和 -0 的,但是 +0 === -0返回true。那么怎么区分+0 和 -0 呢,这也是polyfill Object.is() 需要考虑的问题。

我们可以通过用 +0 和 -0 做分母来区分:

1 / 0 === Infinity // true
1 / -0 === -Infinity // true
1 / 0 === 1 / -0 // false

函数

通过实际开发中我们可以知道,有的函数可以直接调用和使用new调用,有的函数指能通过new调用有的函数只能直接调用。

这背后是有机制在控制的。JS中的数据类型大体分为两种,一个是基本类型,一个是引用类型。那么按理说function不是基本类型那就是引用类型,引用类型就是只有对象,函数也是一个对象,为什么函数这个对象可以调用而其他对象不行呢?

是因为函数这个对象具有两个私有属性决定了上面的特性,我们并没有手段去定义和查看这两个私有属性。一个是 [[constructor]],另一个是 [[call]]。分别决定了是否可以通过new调用和直接调用。

:不能通过new调用函数例如 () => {}箭头函数,不能直接调用的函数例如Image这样的环境提供的函数。

总结于即刻时间重学前端栏目,winter说知识是免费的,教育是收费的,这边总结出来的应该算知识,教育还留在了栏目里。

发布了48 篇原创文章 · 获赞 52 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/letterTiger/article/details/100973432