# 判断数据类型
# typeof
用于判断数据类型,返回值为8个字符串,分别为string、boolean、number、 function 、 object 、undefined、bigint、symbol.
typeof Symbol(); // symbol 有效
typeof undefined; //undefined 有效
typeof new Function(); // function 有效
typeof null; //object 无效
typeof new Date(); //object 无效
// let a=9007199254740995n
// console.log(typeof a)
// //bigint
let b=BigInt("9007199254740995");
console.log(b)
# instanceof
instanceof 运算符原理:用来检测一个对象在其原型链中是否存在一个构造函数的 prototype 属性。
| - | - |
|---|---|
| 语法 | object instanceof constructor |
| 参数 | object(要检测的对象.)constructor(某个构造函数) |
| 描述 | instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。 |
instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性,但它不能检测null 和 undefined
var a=new Array();
console.log(a instanceof Array); // true
var a=new test();
console.log(a instanceof test) //true
var strPrimitive = "I am a string";
typeof strPrimitive; // "string"
console.warn(strPrimitive instanceof String)//FALSE
var strObject = new String( "I am a string" );
typeof strObject; // "object"
console.warn(strObject instanceof String)//TRUE
# constructor检测数据类型法
constructor作用和instanceof非常相似。但constructor检测 Object与instanceof不一样,还可以处理基本数据类型的检测。 不过函数的 constructor 是不稳定的,这个主要体现在把 类的原型进行重写 ,在重写的过程中很有可能出现把之前的constructor给覆盖了,这样检测出来的结果就是不准确的。
var arr=[]
// console.log(arr.__proto__==Array.prototype)
// console.log(arr.constructor)
console.log(arr.__proto__.constructor)//ƒ Array() { [native code] } //__proto__非标准写法
console.log(Object.getPrototypeOf(arr).constructor) // 可以使用这种写法
console.log('abs'.constructor) //或者直接获取构造函数
console.log(Object.getPrototypeOf(1).constructor)
function f1(){
}
function f2(){
}
f2.prototype =new f1()
// f2.prototype.constructor = f2
let f2child = new f2()
console.log(f2child)
console.log(f2child instanceof f2)//true
console.log(f2child instanceof f1)//true
console.log(f2child.constructor)//如果没有及时修正原型指向的构造函数,返回的就是f1,修正了返回是f2
# Object.prototype.toString.call()
Object.prototype.toString.call() 是最准确最常用的方式。
Object.prototype.toString.call('') ; // [object String]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call(new Error()) ; // [object Error]
# 不使用自身的toString()原因
因为toString为Object的原型方法,而Array 、Function等类型作为Object的实例,都重写了toString方法。
var arr=[1,2,3];
console.log(Array.prototype.hasOwnProperty("toString"));//true
console.log(arr.toString());//1,2,3
delete Array.prototype.toString;//delete操作符可以删除实例属性
console.log(Array.prototype.hasOwnProperty("toString"));//false
console.log(arr.toString());//"[object Array]"
# 类的原型重写注意的问题
重写了 prototype 之后发现它的constructor不再指向 Person,而是指向了Object构造函数
在调用构造函数的时候,会为生成的新的实例对象添加一个指针指向构造函数的原型对象,那么在重写prototype的时候我们用对象字面量的方式创建了一个新的对象,而用这种方式创建就相当于调用了Object构造函数
function Person(name, age) {} // Person构造函数是个空函数,将默认值都放在原型对象中
Person.prototype.name = 'xiaoming'
Person.prototype.age = 20
Person.prototype.sayHi = function() {
console.log(`hi, ${this.name}`)
}
const p1 = new Person()
console.log(p1.name)// xiaoming
/*
实例对象的内部指针指向的是构造函数的原型对象
构造函数的prototype属性指向的也是这个原型对象
实例对象和构造函数之间没有直接的联系
*/
console.log(p1.__proto__ === Person.prototype) //true
console.log(p1.constructor) //ƒ Person(name, age) {}
console.log(Person.prototype.constructor) // ƒ Person(name, age) {}
/*
ƒ Person(name, age) {
this.name = name
this.age = age
this.sayHi = function() {
console.log(`hi, ${this.name}`)
}
构造函数的原型对象中的constructor指向的是构造函数
}
*/
// 尝试重写Person构造函数的原型对象
Person.prototype = {
name: 'alice',
age: 12,
sayLove: function() {
console.log(`i love ${this.name}`)
}
}
console.log(Person.prototype.constructor) // ƒ Object() { [native code] }
console.log(p1.name)// xiaoming
const p2 = new Person()
console.log(p2.name)//alice
console.log(p2.__ptoto__)// undefined
那么如何避免这种情况呢?如果constructor真的很重要,那么在重写原型对象的时候可以在对象中加上constructor属性,这样的话就不会去新对象的原型对象中查找constructor属性了
Person.prototype = {
constructor: Person, // 注意这个值不是字符串
name: 'alice',
age: 12,
sayLove: function() {
console.log(`i love ${this.name}`)
}
}
console.log(Person.prototype.constructor)
/*
ƒ Person(name, age) {
this.name = name
this.age = age
this.sayHi = function() {
console.log(`hi, ${this.name}`)
}
}
*/
仍然需要注意的是,在修改了构造函数的原型对象之后,即使给原型对象添加了constructor属性,但是之前通过构造函数生成的实例对象不会自动更新它们的原型对象的指针。请看下方的例子:
//我们可以看一下Person构造函数的原型对象
console.log(Person.prototype) // {name: "alice", age: 12, sayLove: ƒ, constructor: ƒ}
//再来看一下p1的原型对象指针指向的对象
console.log(p1.__proto__) // {name: "xiaoming", age: 20, sayHi: ƒ, constructor: ƒ}
# undefined和null的区别和联系
- 首先看一个判断题:null和undefined 是否相等
console.log(null==undefined)//true
console.log(null===undefined)//false
观察可以发现:null和undefined 两者相等,但是当两者做全等比较时,两者又不等。
原因:null: Null类型,代表“空值”,代表一个空对象指针,使用typeof运算得到 “object”,可认为它是一个特殊的对象值。(实际历史原因造成)
undefined: Undefined类型,当一个声明了一个变量未初始化时,得到的就是undefined。
实际上,undefined值可近似认为是派生自null值的(理解记忆),ECMAScript标准规定对二者进行相等性测试要返回true是因为历史原因和兼容性
那到底什么时候是null,什么时候是undefined呢?
null表示"没有对象",即该处不应该有值。典型用法是:
作为函数的参数,表示该函数的参数不是对象。
作为对象原型链的终点。
undefined表示"缺少值",就是此处应该有一个值,但是还没有定义。典型用法是:
变量被声明了,但没有赋值时,就等于undefined。
调用函数时,应该提供的参数没有提供,该参数等于undefined。
对象没有赋值的属性,该属性的值为undefined。
函数没有返回值时,默认返回undefined。
function foo(arg1 = arg2, arg2) {
console.log(`${arg1} ${arg2}`);
}
// 在上面 foo 函数中,如果第一个参数没有传,将会使用第二个参数作为第一个实参值。调用:
foo('arg1', 'arg2') // arg1 arg2
// 返回内容正常,但是当第一个参数缺省时,执行 arg1 = arg2 会当作暂时性死区处理:
// 因为除了块级作用域以外,函数参数默认值也会受到 TDZ 影响。
foo(undefined, 'arg2') // Uncaught ReferenceError: arg2 is not defined
// 看看下面的代码会输出什么?
// 这就涉及到 undefined 和 null 的区别了。在执行 foo(null, 'arg2') 时,
// 不会认为「函数第一个参数缺省」,而会直接接受 null 作为第一个参数值。
foo(null, 'arg2') // null arg2
# 兼容ie6处理数据类型
const toString = Object.prototype.toString;
export function type(x, strict = false) {
strict = !!strict;
// fix typeof null = object
if(x === null){
return 'null';
}
const t = typeof x;
// number string boolean undefined symbol
if(t !== 'object'){
return t;
}
let cls;
let clsLow;
try {
cls = toString.call(x).slice(8, -1);
clsLow = cls.toLowerCase();
} catch(e) {
// ie 下的 activex 对象
return 'object';
}
if(clsLow !== 'object'){
// 区分 String() 和 new String()
if (strict && (clsLow === 'number' || clsLow === 'boolean' || clsLow === 'string')) {
return cls;
}
return clsLow;
}
if(x.constructor == Object){
return clsLow;
}
// Object.create(null)
try {
// __proto__ 部分早期 firefox 浏览器
if (Object.getPrototypeOf(x) === null || x.__proto__ === null) {
return 'object';
}
} catch(e) {
// ie 下无 Object.getPrototypeOf 会报错
}
// function A() {}; new A
try {
const cname = x.constructor.name;
if (typeof cname === 'string') {
return cname;
}
} catch(e) {
// 无 constructor
}
// function A() {}; A.prototype.constructor = null; new A
return 'unknown';
}
# toLocaleString (数字千位符)
# Number.prototype.toLocaleString()
toLocaleString() 方法返回这个数字在特定语言环境下的表示字符串。
在没有指定区域的基本使用时,返回使用默认的语言环境和默认选项格式化的字符串。
var number = 3500;
console.log(number.toLocaleString()); // Displays "3,500" if in U.S. English locale