// Function 实例
function fn1() {}
var fn2 = function() {}
var fn3 = new Function('console.log("Hi, everyone")') // 一般不使用 Function 构造器去生成 Function 对象,相比函数声明或者函数表达式,它表现更为低效。
// Object 实例
var obj1 = {}
var obj2 = new Object()
var obj3 = new fn1()
var obj = Object.create(null)
var obj1 = Object.create(null)
var obj2 = {}
obj.__proto__ === undefined // true
obj.getPrototypeOf() // 抛出错误 TypeError: obj.getPrototypeOf is not a function
今天又又又...又整理了一下,那些 JavaScript 里不清不楚的知识点。
一、数据类型的分类
截止发文日期,ECMAScript 标准的数据类型仅有 8 种(ECMAScript Language Types)。可以分为两类:
typeof(instance) === 'object'
)typeof(instance) === 'symbol'
)typeof(instance) === 'bigint'
)原始类型的比较的是值,只有两者的值相等,那么它们被认为是相等的,否则不相等。而引用类型比较的是地址,当两者的标识符同时指向内存的同一个地址,则被认为是相等的,否则不相等。
二、原始类型与原始值
所有基本类型的值(即原始值,Primitive Values)都是不可改变(immutable)的,而且不含任何属性和方法的。
到这里可能会有小伙伴打问号了???
Q1:原始类型与原始值有什么区别?
Q2:原始值不可改变?这样不是改变了吗?
基本类型值可以被替换,但不能被改变。
再有示例:
Q3:原始值没有任何属性和方法?那这个是怎么回事?
在 JavaScript 中除了
null
和undefined
之外,所有的基本类型都有其对应的包装对象(Wrapper Object)。因此,访问null
或undefined
的任何属性和方法都会抛出错误。String
为字符串基本类型。Number
为数值基本类型。BigInt
为大整数基本类型。Boolean
为布尔基本类型。Symbol
为字面量基本类型。这些包装对象的
valueOf
方法返回其对应的原始值。不是说好的,原始值不含任何的属性和方法吗?那
foo.length
和foo.toUpperCase()
是咋回事啊???其实它内部是这样实现的:当字符串字面量调用一个字符串对象才有的方法或属性时,JavaScript 会自动将基本字符串转化为字符串对象并且调用相应的方法或属性。(Boolean 和 Number 也同样如此)。
我们尝试在控制台上打印一下
new String('foo')
,可以看到该实例对象有一个length
属性,其值为3
,实例对象本身没有toUpperCase()
方法,所以接着往原型上查找,果然找到了。(由于原型上方法太多,截图里没有展开,否则影响文章篇幅)因此
可下面为什么
length
还是3
呢?三、对象
在 JavaScript 中,除了以上的原始值,其余都属于对象。
与原始类型不同的是,对象是可变(mutable)的。
1. 对象的分类
我们可以将对象划分为普通对象(ordinary object)和函数对象(function object)。
那怎样区分呢?我们先定义一些
Function
实例和Object
实例:我们来打印一下结果:
Object
和Function
本身就是 JavaScript 中自带的函数对象。其中obj1
、obj2
、obj3
为普通对象(均为Object
的实例),而fn1
、fn2
、fn3
为函数对象(均是Function
的实例)。记住以下这句话:
2. 对象的原型
接着,引入两个很容易让人抓狂、混淆的两兄弟
prototype
(原型对象)和__proto__
(原型)。这俩兄弟的主要是为了构造原型链而存在的。因此有以下结论:
再上几个菜,请慢慢品尝:
在 JavaScript 中访问一个对象属性,它在原型上是怎样查找的呢?
我们可以在控制台打印一下,看下两者的区别。
JavaScript 常被描述为一种基于原型的语言 —— 每个对象拥有一个原型(
[[Prototype]]
),对象以其原型为模板、从原型继承方法和属性。原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链(prototype chain)。3. 继承
关于继承内容,可看另外一篇文章:深入 JavaScript 继承原理。
4. 对象的内部属性(Internal properties)
在规范中,对象的内部方法和内部插槽使用双方括号
[[]]
中包含的名称标识,且首字母为大写。例如[[Prototype]]
、[[Class]]
、[[Extensible]]
、[[Call]]
、[[Scopes]]
、[[FunctionLocation]]
等等。下面挑几个来讲一下:
4.1 [[Class]]
[[Class]]
是对象的一个内部属性,其值为以下字符串之一:Function
、Object
、Array
、Boolean
、Number
、String
、Symbol
、RegExp
、JSON
、Date
、Math
、Error
、Arguments
等。BigInt
、Set
、WeakSet
、Map
、WeakMap
、Reflect
、Promise
、GeneratorFunction
、AsyncFunction
、Window
、Intl
、WebAssembly
,以及派生于HTMLElement
的(如HTMLScriptElement
)等等。我们都知道
typeof
无法判断对象的具体类型,无论是typeof {}
、typeof []
、还是typeof Math
都返回"object"
。但有了[[Class]]
属性之后,我们就可以利用它来判断对象的类型了。访问[[Class]]
的唯一方法是通过默认的toString()
方法(该方法是通用的):以下封装了获取对象类型的方法:
4.2 [[Construct]]
一个对象里,如若没有
[[construct]]
属性,是无法使用new
关键字进行构造的。四、类型转换
在 JavaScript 中,我们会经常使用相等运算符(
==
)去比较两个操作数是否相等。当两个操作数一个是引用类型,另一个是原始类型的时候,前者会先转换为原始类型,再比较。那么,引用类型是如何转换为原始类型的呢?
未完待续...
参考