JavaScript中typeof,instanceof,hasOwnProperty,in的用法和区别

一. typeof操作符

typeof操作符用于返回正在使用值的类型。

// 使用原始值
let mNull = null;
let mUndefined = undefined;
let mString = 'mazey';
let mNumber = 123;
let mBoolean = true;
let mFunction = function () {
    return true;
};

// 用构造函数的方式new一个实例
let oString = new String('cherrie');
let oRegExp = new RegExp('^[0-9]+$');
let oFunction = new Function('x', 'y', 'return x + y');

let oObj = {};
let oNew = new Object();

// typeof值
console.log(typeof mNull); // object
console.log(typeof mUndefined); // undefined
console.log(typeof mString); // string
console.log(typeof mNumber); // number
console.log(typeof mBoolean); // boolean
console.log(typeof mFunction); // function
console.log(typeof oString); // object
console.log(typeof oRegExp); // object
console.log(typeof oFunction); // function
console.log(typeof oObj); // object
console.log(typeof oNew); // object

在《JavaScript启示录》中new RegExp()介绍会返回function,但是事实上我在chrome控制台中看到的是object

于是我console.log(new RegExp('^[0-9]+$')),打印出来的是字符串/^[0-9]+$/

console.log(new RegExp('^[0-9]+$')); // /^[0-9]+$/
console.log(RegExp); // ƒ RegExp() { [native code] } 原始值
console.log(String); // ƒ String() { [native code] } 原始值
console.log(/^[0-9]+$/); // /^[0-9]+$/
console.log(new RegExp('^[0-9]+$') === /^[0-9]+$/); // false
console.log(RegExp('^[0-9]+$') === /^[0-9]+$/); // false

综上可以看出现版本RegExpString Number一样属于JavaScript的原始值。

Math作为JavaScript中的静态对象回返回什么呢?

console.log(typeof Math); // object
console.log(typeof Math.PI); // number
console.log(typeof Math.ceil); // function

所以Math__proto__还是Objecttypeof还能返回对象的属性和方法的类型。

typeof使用场景

  1. 判断某个变量是否已定义
console.log(typeof aaa); // 'undefined'

// 判断
if (typeof bbb === 'undefined') {
    console.log('变量未定义');
}
  1. 区分原始值和复杂值(对象值)

因为复杂值往往返回object,当然有个例外就是原始值里面的null也返回object,然后function作为Object的实例也是复杂值。

// 判断是否时复杂值(对象值)
function isObject (m) {
    return (typeof m === 'function' || (typeof m === 'object' && m !== null));
}

console.log(isObject(new RegExp('123'))); // true
console.log(isObject('123')); // false
console.log(isObject(String('123'))); // false
console.log(isObject(null)); // false

// 判断是否是原始值
function isNative (m) {
    return (m === null || (typeof m !== 'object' && typeof m !== 'function'));
}

console.log(isNative(new RegExp('123'))); // false
console.log(isNative('123')); // true
console.log(isNative(String('123'))); // true
console.log(isNative(null)); // true
  1. 检测某个变量是否是函数

当使用闭包时判断是函数后再进行下一步。

function qqq () {
    let a = 0;
    let b = function () {
        a++;
        console.log(a);
    };
    return b;
}

let ccc = qqq();
console.log(typeof ccc); // function
if (typeof ccc === 'function') {
    ccc(); // 1
    ccc(); // 2
    ccc(); // 3
    ccc(); // 4
}

二. instanceof操作符

通过使用instanceof操作符,可以确定一个对象是否是特定构造函数实例,返回truefalse

instanceof只适用于构造函数创建返回的复杂对象实例

任何时间判断一个对象(复杂值)是否是Object的实例时,它都将返回true,因为所有对象都继承自Object()构造函数。

let oFather = function () {
    this.firstName = 'mazey';
};
oFather.prototype.lastName = 'qian';

// 实例
let oSon = new oFather();
console.log(oSon instanceof oFather); // true

// 继承
let nFather = function () {};
nFather.prototype = new oFather();
nFather.construction = nFather;
console.log(nFather.firstName); // undefined
console.log(nFather.prototype.lastName); // qian
console.log(nFather instanceof oFather); // false
console.log(new nFather() instanceof nFather); // true

// 相对于Object来说
console.log('123' instanceof Object); // false
console.log(new String('123') instanceof Object); // true 构造出来的实例
console.log(null instanceof Object); // false

instanceof使用场景

判断在一个继承关系中实例是否属于它的父类。

// 继承
let oFather = function () {};
let nFather = function () {};
nFather.prototype = new oFather();
nFather.construction = nFather;

let nSon = new nFather();
console.log(nSon instanceof nFather); // true
console.log(nSon instanceof oFather); // true

三. in操作符和hasOwnProperty方法

in操作符可以检查一个对象的属性,包括来自原型链的属性,hasOwnProperty()方法可以检查来自非原型链属性的对象。

例如现在有一个对象let obj = {name: 'mazey'};name是它自身定义的属性,toString是它从原型链上继承下来的属性。

let obj = {name: 'mazey'};
console.log('name' in obj); // true
console.log('toString' in obj); // true
console.log('name' in Object); // true
console.log(obj.hasOwnProperty('name')); // true
console.log(obj.hasOwnProperty('toString')); // false
console.log(Object.hasOwnProperty('name')); // true

所以in操作符查找的范围更广一点,可以用hasOwnProperty()判断是否是对象自身的属性,而不是通过类似obj.prototype.foo = 'foo';这样定义的。

hasOwnProperty方法使用场景

在实际项目中经常使用for...in...来遍历对象中可枚举的属性,但是for...in...常常把原型obj.prototype.xxx中的属性也列举出来,所以在循环的时候可以加上hasOwnProperty()方法判断下。

function obj0 () {
    this.name = 'mazey',
    this.age = '24'
};
obj0.prototype.gender = 'male';
let obj1 = new obj0();

// 打印所有可枚举属性
for (let key in obj1) {
    console.log(key); // name age gender 从原型链上继承下来的属性也会被打印出来
}

// 过滤掉原型链上的属性
for (let key in obj1) {
    if (obj1.hasOwnProperty(key)) {
        console.log(key); // name age
    }
}

四. 总结

  1. typeof可以判断使用值的类型,注意null返回object
  2. instanceof验证构造函数构造出来的实例,可以用来判断一个对象是否属于一个父类。
  3. hasOwnProperty方法常常与in操作符搭配使用,用来遍历一个对象自身的属性。

五. 相关扩展

1. 删除对象的属性

若想把一个对象的自身属性完全删除,要使用delete操作符。

let obj0 = {
    name: 'mazey',
    age: 24
};

// 删除age属性
delete obj0.age;
console.log(obj0.hasOwnProperty('age')); // false
console.log('age' in obj0); // false

// 试着删除原型链上的属性 toString
delete obj0.toString
console.log('toString' in obj0); // true

需要注意的是delete不会删除原型链上的属性。

2. 可枚举

每个对象的属性都分为可枚举和不可枚举属性,可以使用propertyIsEnumerable()方法来检查哪些属性是可枚举的。

每个对象都有一个propertyIsEnumerable方法。此方法可以确定对象中指定的属性是否可以被for...in(for...in语句以任意顺序遍历一个对象的可枚举属性。对于每个不同的属性,语句都会被执行)循环枚举,但是通过原型链继承的属性除外。如果对象没有指定的属性,则此方法返回false

有的资料说只要能被for..in遍历的属性就是可枚举的,实际上要排除从原型链上继承下来的属性,只有自身的属性是可枚举的。

// 第一个构造函数
function ConFun0 () {
    this.firstName = 'mazey';
}
ConFun0.prototype.firstCom = 'bang';

// 第二个构造函数
function ConFun1 () {
    this.secondName = 'qian';
}
// 继承第一个
ConFun1.prototype = new ConFun0();
ConFun1.prototype.constructor = ConFun1;

// 实例
let obj = new ConFun1();
obj.girlName = 'cherrie';

// 是否可枚举
console.log(obj.propertyIsEnumerable('constructor')); // false
console.log(obj.propertyIsEnumerable('firstName')); // false
console.log(obj.propertyIsEnumerable('firstCom')); // false
// 通过原型链继承的属性不是可枚举
console.log(obj.propertyIsEnumerable('secondName')); // true
console.log(obj.propertyIsEnumerable('girlName')); // true

for (let key in obj) {
    console.log(key); // secondName girlName (原型链上的属性也会被打印出来->) firstName constructor firstCom
}

console.log(`---分割线---`);

for (let key in obj) {
    // 过滤掉原型链上的属性
    if (obj.hasOwnProperty(key)) {
        console.log(key); // secondName girlName
    }
}

所以可枚举的属性一定能被for..in循环遍历出来,但是for...in循环遍历出来的属性不一定是可枚举的,需排除从原型链上继承下来的属性,这里可以通过hasOwnProperty()方法过滤掉不可枚举属性。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272