null 与 undefined 为js中两种不同的数据类型
但是 typeof undefined 的结果是 undefined,typeof null 的结果是 object
从字面含义来说,undefined 的意思是未定义,即应该有值但是没有值,比如说我们访问一个对象里没有的属性,从人类本身的意愿来说,我们觉得他是有值的,而且能可以得到预期结果,但是他偏偏没有这个属性; 又或是函数的形参,当没有实参传入的时候
function fn(parameter) {
console.log(parameter)
}
当我们不传参数调用时
fn()
控制台将会输出 undefined
fn(null)
又或者调用一个函数没有返回值的时候,
let res = fn()
console.log(res)
也会输出 undefined
null
null 的意思是 ‘空值’,空值不是没有,undefined 才是没有,不存在
如果我们想看见 null ,那么就必须手动赋予 null 类型
坑点
虽说typeof null 的结果是 object,但是他并不算是对象,他没有原型
console.dir(null)
将会输出 null
MDN 对此方法的描述
可以用次方法打印dom元素,将会看到一长串属性,可见大
常见的 offsetTop, offsetWidth 等属性都作为基本属性存在于此
再说 console.log(null instanceof Object),
输出 false, 更加证实 null 不是对象
instanceof
MDN
instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。
翻译一下MDN的意思: A instanceof B A对象的的原型链上是否有B的prototype属性
A必须是一个对象实例, 可以是数组和对象的字面量形式,可以是原始数据类型的 new String('str') 形式
也就是说
let simpleStr = 'hello'
simpleStr instanceof String // 返回false, 非对象实例
let objStr = new String('hello')
objStr instanceof String // 返回true
console.log([] instanceof Array)
console.log([] instanceof Object)
都会输出 true
这是为什么呢?????
之前提到的 console.dir 分别打印原生构造函数 Array 和 Object
原生构造函数 还包括
Number,Boolean,String等, 都可以直接使用new调用
console.dir(Array)
console.dir(Object)
看数组的 prototype 属性,上边都是眼熟的方法呀,这就是能够直接调用数组方法的原因 --原型链
在数组的 prototype 上有个 __proto__ 上面是一个 Object, 他正好等于 console.dir(Object) 的prototype
因此
前面的数组的两个 instanceof 都是 true
- 文章写起来没完啊。。。。。
再说 constructor
可以用来检测数据类型
console.log(({}).constructor === Object) // true
console.log([].constructor === Array) // true
因为
{ }会被当做一个代码块,所以这里加个小括号
.constructor 将会打印一个对象的构造函数
例如
function AS () {
}
let ui = new AS()
console.log(ui.constructor)
因此之前打印的 数组或者对象的 .constructor 就是原生构造函数
说到数据类型检测,最最安全的方法其实是 Object.prototype.toString
下面直接放出 elementUI的源码
export function isString(obj) {
return Object.prototype.toString.call(obj) === '[object String]';
}
export function isObject(obj) {
return Object.prototype.toString.call(obj) === '[object Object]'
}
export function isHtmlElement(node) {
return node && node.nodeType === Node.ELEMENT_NODE
}
export const isFunction = (functionToCheck) => {
var getType = {}
return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]'
}
export const isUndefined = (val)=> {
return val === void 0
}
export const isDefined = (val) => {
return val !== undefined && val !== null
}
核心就是常见的 toString 方法,但是呢该方法会被 array,number 等数据类型的构造函数重写, 被重写之后,可以找到最近的 toString 方法,就不会沿着原型链一层一层查找了,
因此 源码直接使用 Object原型上的方法
你可能会有个疑惑,为什么要加 .call, 嗯哼,我在最初看的时候也有这个疑惑, 哈哈哈
这个东西并不复杂,别忘了 toString 是个函数,要让被检测的数据类型,调用这个方法,调用函数的方式就是 a.fn(), 这里很明显 toString 是被 Object 调用,但是我们想让它被其他对象调用
因此
.call 来了, 他会改变函数的调用者,第一个参数就是被检测是变量
至于 .bind, .apply 都是大同小异,这里不再讲述,
我是怎么从 null 与 undefined 说道源码的 .............., 这之间的关联好多啊,不知不觉各种小知识串联起来,构成了javascript的基础知识大杂烩