typeof
typeof 操作符返回一个字符串,表示未经计算的操作数的类型。
语法
typeof 运算符后接操作数:
typeof operand
typeof(operand)
参数
operand 一个表示对象或原始值的表达式,其类型将被返回。
描述
下面总结了 typeof 可能的返回值。有关类型和原始值的更多信息,可查看 JavaScript 数据结构 页面。
类型 | 结果 |
Undefined | “undefined” |
Null | “object” (见下文) |
Boolean | “boolean” |
Number | “number” |
BigInt (ECMAScript 2020 新增) | “bigint” |
String | “string” |
Symbol (ECMAScript 2015 新增) | “symbol” |
宿主对象(由 JS 环境提供) | 取决于具体实现 |
Function 对象 (按照 ECMA-262 规范实现 [[Call]]) | “function” |
其他任何对象 | “object” |
原始值
除对象类型(object)以外的其它任何类型定义的不可变的值(值本身无法被改变)。例如(与 C 语言不同),JavaScript 中字符串是不可变的(译注:如,JavaScript 中对字符串的操作一定返回了一个新字符串,原始字符串并没有被改变)。我们称这些类型的值为“原始值”。
特性
1、typeof 总是返回一个字符串。
2、typeof 能正确判断原始值的类型,null 除外;引用类型数据能正确判断 Function、函数的类型,其他的都会返回 ‘object’。
示例
// 数值console.log(typeof 37 === ‘number’) // trueconsole.log(typeof 3.14 === ‘number’) // trueconsole.log(typeof (42) === ‘number’) // trueconsole.log(typeof Math.LN2 === ‘number’) // trueconsole.log(typeof Infinity === ‘number’) // trueconsole.log(typeof NaN === ‘number’) // true 尽管它是 “Not-A-Number” (非数值) 的缩写console.log(typeof Number(1) === ‘number’) // true Number 会尝试把参数解析成数值console.log(typeof 42n === ‘bigint’) // true// 字符串console.log(typeof ” === ‘string’) // trueconsole.log(typeof ‘bla’ === ‘string’) // trueconsole.log(typeof `template literal` === ‘string’) // trueconsole.log(typeof ‘1’ === ‘string’) // true 注意内容为数字的字符串仍是字符串console.log(typeof (typeof 1) === ‘string’) // true typeof 总是返回一个字符串console.log(typeof String(1) === ‘string’) // true String 将任意值转换为字符串,比 toString 更安全// 布尔值console.log(typeof true === ‘boolean’) // trueconsole.log(typeof false === ‘boolean’) // trueconsole.log(typeof Boolean(1) === ‘boolean’) // Boolean() 会基于参数是真值还是虚值进行转换console.log(typeof !!(1) === ‘boolean’) // true 两次调用 ! (逻辑非) 操作符相当于 Boolean()// Symbolsconsole.log(typeof Symbol() === ‘symbol’) // trueconsole.log(typeof Symbol(‘foo’) === ‘symbol’) // trueconsole.log(typeof Symbol.iterator === ‘symbol’) // true// Undefinedconsole.log(typeof undefined === ‘undefined’) // trueconsole.log(typeof declaredButUndefinedVariable === ‘undefined’) // trueconsole.log(typeof undeclaredVariable === ‘undefined’) // true// 对象console.log(typeof { a: 1 } === ‘object’) // true// 使用 Array.isArray 或者 Object.prototype.toString.call// 区分数组和普通对象console.log(typeof [1, 2, 4] === ‘object’) // trueconsole.log(typeof new Date() === ‘object’) // trueconsole.log(typeof /regex/ === ‘object’) // true 历史结果请参阅正则表达式部分// 使用 new 操作符// 除 Function 外的所有构造函数的类型都是 ‘object’var func = new Function()console.log(typeof func) // 返回 ‘function’var A = function() {}var b = new A()console.log(typeof b) // 返回 ‘object’// 下面的例子令人迷惑,非常危险,没有用处。避免使用它们。console.log(typeof new Boolean(true) === ‘object’) // trueconsole.log(typeof new Number(1) === ‘object’) // trueconsole.log(typeof new String(‘abc’) === ‘object’) // true// 函数console.log(typeof function () { } === ‘function’) // trueconsole.log(typeof class C { } === ‘function’) // trueconsole.log(typeof Math.sin === ‘function’) // true// Null// JavaScript 诞生以来便如此console.log(typeof null === ‘object’) // true
在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此,null 的类型标签是 0,typeof null 也因此返回 “object”。(参考来源)
console.log(typeof 0); // numberconsole.log(typeof BigInt(Number.MAX_SAFE_INTEGER)); // bigintconsole.log(typeof ‘0’); // stringconsole.log(typeof true); // booleanconsole.log(typeof undefined); // undefinedconsole.log(typeof function () { }); // functionconsole.log(typeof Symbol); // functionconsole.log(typeof Symbol()); // symbolconsole.log(typeof Date); // functionconsole.log(typeof Date()); // stringconsole.log(typeof new Date); // objectconsole.log(typeof new Date()); // objectconsole.log(typeof RegExp); // functionconsole.log(typeof RegExp()); // objectconsole.log(typeof new RegExp); // objectconsole.log(typeof new RegExp()); // objectconsole.log(typeof []); // objectconsole.log(typeof {}); // objectconsole.log(typeof null); // object
如果我们想判断一个对象的正确类型,可以考虑使用 instanceof,因为内部机制是通过 判断实例对象的 __proto__ 和生成该实例的构造函数的 prototype 是不是引用的同一个地址(也就是原型链的方式)来判断的。
错误
在 ECMAScript 2015 之前,typeof 总能保证对任何所给的操作数返回一个字符串。即便是没有声明的标识符,typeof 也能返回 ‘undefined’。使用 typeof 永远不会抛出错误。
但在加入了块级作用域的 let 和 const 之后,在其被声明之前对块中的 let 和 const 变量使用 typeof 会抛出一个 ReferenceError。块作用域变量在块的头部处于“暂存死区”,直至其被初始化,在这期间,访问变量将会引发错误。
typeof undeclaredVariable === ‘undefined’;typeof newLetVariable; // ReferenceErrortypeof newConstVariable; // ReferenceErrortypeof newClass; // ReferenceErrorlet newLetVariable;const newConstVariable = ‘hello’;class newClass{};
instanceof
instanceof 运算符用于检测构造函数 prototype 属性是否出现在某个实例对象的原型链上。可以用来判断都属于 Object 类型和一些特殊情况的对象,如:数组和对象,但不能用于基础数据类型。
语法
object instanceof constructor
参数
object 某个实例对象
constructor 某个构造函数
描述
instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。
示例
B instanceof A:判断 B 是否为 A 的实例,可以用于继承关系中
function Car(make, model, year) { this.make = make; this.model = model; this.year = year;}const auto = new Car(‘Honda’, ‘Accord’, 1998);console.log(auto instanceof Car);// expected output: trueconsole.log(auto instanceof Object);// expected output: true// 定义构造函数function C(){}function D(){}var o = new C();o instanceof C; // true,因为 Object.getPrototypeOf(o) === C.prototypeo instanceof D; // false,因为 D.prototype 不在 o 的原型链上o instanceof Object; // true,因为 Object.prototype.isPrototypeOf(o) 返回 trueC.prototype instanceof Object // true,同上C.prototype = {};var o2 = new C();o2 instanceof C; // trueo instanceof C; // false,C.prototype 指向了一个空对象,这个空对象不在 o 的原型链上.D.prototype = new C(); // 继承var o3 = new D();o3 instanceof D; // trueo3 instanceof C; // true 因为 C.prototype 现在在 o3 的原型链上
需要注意的是,如果表达式 obj instanceof Foo 返回 true,则并不意味着该表达式会永远返回 true,因为 Foo.prototype 属性的值有可能会改变,改变之后的值很有可能不存在于 obj 的原型链上,这时原表达式的值就会成为 false。另外一种情况下,原表达式的值也会改变,就是改变对象 obj 的原型链的情况,虽然在目前的ES规范中,我们只能读取对象的原型而不能改变它,但借助于非标准的 __proto__ 伪属性,是可以实现的。比如执行 obj.__proto__ = {} 之后,obj instanceof Foo 就会返回 false 了。
A 是 B 的父对象,c 是 B 的实例,c instanceof A 与 c instanceof B 结果均为 true。
function A() { }function B() { }B.prototype = new A()const c = new B()console.log(c instanceof B); // trueconsole.log(c instanceof A); // trueconsole.log(A instanceof B); // falseconsole.log(B instanceof A); // falseconsole.log(B.__proto__ === A.prototype); // falseconsole.log(B.prototype.__proto__ === A.prototype); // trueconsole.log(c instanceof Object); // true c 属于console.log(B instanceof Object); // trueconsole.log(A instanceof Object); // true
演示 String 对象和 Date 对象都属于 Object 类型和一些特殊情况
下面的代码使用了 instanceof 来证明:String 和 Date 对象同时也属于Object 类型(他们是由 Object 类派生出来的)。
但是,使用对象文字符号创建的对象在这里是一个例外:虽然原型未定义,但 instanceof Object 返回 true。
var simpleStr = “This is a simple string”;var myString = new String();var newStr = new String(“String created with constructor”);var myDate = new Date();var myObj = {};var myNonObj = Object.create(null);console.log(simpleStr instanceof String) // 返回 false, 非对象实例,因此返回 falseconsole.log(myString instanceof String) // 返回 trueconsole.log(newStr instanceof String) // 返回 trueconsole.log(myString instanceof Object) // 返回 trueconsole.log(myObj instanceof Object) // 返回 true, 尽管原型没有定义console.log(({}) instanceof Object) // 返回 true, 同上console.log(myNonObj instanceof Object) // 返回 false, 一种创建非 Object 实例的对象的方法console.log(myString instanceof Date) // 返回 falseconsole.log(myDate instanceof Date) // 返回 trueconsole.log(myDate instanceof Object) // 返回 trueconsole.log(myDate instanceof String) // 返回 false
Object.prototype.toString.call(object)/Object.prototype.toString.apply(object)
toString.call() 或 toString.apply() 方法几乎可以精准判断各类数据的类型。
console.log(Object.prototype.toString.call(“kevin”)) // [object String]console.log(Object.prototype.toString.call(18)) // [object Number]console.log(Object.prototype.toString.call(true)) // [object Boolean]console.log(Object.prototype.toString.call(undefined)) // [object Undefined]console.log(Object.prototype.toString.call(null)) // [object Null]console.log(Object.prototype.toString.call(NaN)) // [object Number]console.log(Object.prototype.toString.call({ name: “kevin” })) // [object Object]console.log(Object.prototype.toString.call(function () { })) // [object Function]console.log(Object.prototype.toString.call([])) // [object Array]console.log(Object.prototype.toString.call(new Date)) // [object Date]console.log(Object.prototype.toString.call(/d/)) // [object RegExp]console.log(Object.prototype.toString.call(Math)) // [object Math]function Person() { }console.log(Object.prototype.toString.call(new Person)) // [object Object]var o = { [Symbol.toStringTag]: “A” }console.log(Object.prototype.toString.call(o)) // [object A]console.log(window.toString()) // “[object Window]”console.log(Object.prototype.toString.call(window)) // “[object Window]”console.log(Object.prototype.toString.call(Symbol())); // “[object Symbol]”// 封装function getTypeof(data) { let dataType = Object.prototype.toString.call(data); return dataType.slice(8, -1)}console.log(getTypeof(18)) // Numberconsole.log(getTypeof(“kevin”)) // Stringconsole.log(getTypeof(new Date)) // Dateconsole.log(getTypeof([])) // Arrayconsole.log(getTypeof({})) // Objectfunction Person() { }console.log(getTypeof(new Person)) // Objectconsole.log(getTypeof(new Person())) // Object
语法
obj.toString()
返回值
一个表示该对象的字符串
描述
每个对象都有一个 toString() 方法,当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用。默认情况下,toString() 方法被每个 Object 对象继承。如果此方法在自定义对象中未被覆盖,toString() 返回 “[object type]”,其中 type 是对象的类型。以下代码说明了这一点:
var o = new Object();console.log(o.toString()); // [object Object]
备注:如 ECMAScript 5 和随后的 Errata 中所定义,从 JavaScript 1.8.5 开始,toString() 调用 null 返回[object Null],undefined 返回 [object Undefined]。请参阅下面的使用toString()检测对象类型。
示例
覆盖默认的 toString 方法
可以自定义一个方法,来取代默认的 toString() 方法。该 toString() 方法不能传入参数,并且必须返回一个字符串。自定义的 toString() 方法可以是任何我们需要的值,但如果它附带有关对象的信息,它将变得非常有用。
以下代码定义了 Dog 对象类型,并创建了一个 Dog 类型的 theDog 对象:
function Dog(name,breed,color,sex) { this.name = name; this.breed = breed; this.color = color; this.sex = sex;}var theDog = new Dog(“Gabby”, “Lab”, “chocolate”, “female”);// 如果当前的对象调用了 toString() 方法,它将会返回从 Object继承而来的 toString() 方法的返回默认值:console.log(theDog.toString()); // 返回 [object Object]// 下面的代码中定义了一个叫做 dogToString() 的方法来覆盖默认的 toString() 方法。// 这个方法生成一个 “property = value;” 形式的字符串,该字符串包含了当前对象的 name、breed、color 和 sex 的值。Dog.prototype.toString = function dogToString() { var ret = “Dog ” + this.name + ” is a ” + this.sex + ” ” + this.color + ” ” + this.breed; return ret;}// 也可以这样写Dog.prototype.toString = function dogToString() { return `Dog ${this.name} is a ${this.sex} ${this.color} ${this.breed}`;}// 使用上述代码,任何时候在字符串上下文中使用 theDog.toString() 时,// JavaScript 都会自动调用 dogToString() 方法(dogToString() 可以是一个匿名函数),并且返回以下字符串:// “Dog Gabby is a female chocolate Lab”console.log(theDog.toString()); // 返回 “Dog Gabby is a female chocolate Lab”// 也可以这样写Dog.prototype.toString = function dogToString() { return ‘[object Dog]’;}Dog.prototype[Symbol.toStringTag] = ‘Dog’// 也可以这样写theDog[Symbol.toStringTag] = ‘Dog’console.log(theDog.toString()); // 返回 [object Dog]console.log(Dog.prototype.toString()); // 返回 [object Dog]console.log(Dog.prototype.toString.call(theDog)); // 返回 [object Dog]console.log(Object.prototype.toString.call(theDog)); // 返回 [object Object]
使用 toString() 检测对象
可以通过 toString() 来获取每个对象的类型。为了每个对象都能通过 Object.prototype.toString() 来检测,需要以 Function.prototype.call() 或者 Function.prototype.apply() 的形式来调用,传递要检查的对象作为第一个参数,称为 thisArg。
var toString = Object.prototype.toString;toString.call(new Date); // [object Date]toString.call(new String); // [object String]toString.call(Math); // [object Math]//Since JavaScript 1.8.5toString.call(undefined); // [object Undefined]toString.call(null); // [object Null]
检测原理
Object.prototype.toString.call(obj) 类型检测的原理是什么?首先我们来看一下 toString() 方法:
var num = 1console.log(num.toString()) // ‘1’var str = ‘kevin’console.log(str.toString()) // ‘kevin’var bool = falseconsole.log(bool.toString()) // ‘false’var arr = [1, 2, 3]console.log(arr.toString()) // ‘1,2,3’var obj = { name: ‘kevin’ }console.log(obj.toString()) // ‘[object Object]’var fn = function(){}console.log(fn.toString()) // ‘function(){}’console.log(JSON.toString()) // ‘[object JSON]’console.log(Atomics.toString()) // ‘[object Atomics]’console.log(null.toString()) // Cannot read property ‘toString’ of nullconsole.log(undefined.toString() // Cannot read property ‘toString’ of undefinedconsole.log(window.toString()) // ‘[object Window]’
从以上示例可以知道 toString 是将数据转换为字符串(null 和 undefined 除外),并且各种类型的数据转换为字符串的方式又不一样。即若参数不为 null 或 undefined,则将参数转为对象,再作判断。对于原始类型,转为对象的方法即装箱。
转为对象后,取得该对象的 [Symbol.toStringTag] 属性值(可能会遍历原型链)作为 tag,如无该属性,或该属性值不为字符串类型,则依下表取得 tag,然后返回 “[object ” + tag + “]” 形式的字符串。
新标准引入了 [Symbol.toStringTag] 属性,是为了把此方法接口化,用于规范新引入的对象对此方法的调用。但对于“老旧”的对象,就只能直接输出值,以保证兼容性。
// 1. 三个容器对象。这类对象用作命名空间,用于存储同一类方法。JSON[Symbol.toStringTag]; // => “JSON”Math[Symbol.toStringTag]; // => “Math”Atomics[Symbol.toStringTag]; // => “Atomic”// 这三个对象的 toString() 都没有重写,直接调用 toString() 方法也可以得到相同的结果。JSON.toString(); // => “[object JSON]”Math.toString(); // => “[object Math]”Atomics.toString(); // => “[object Atomics]”// 2. 两个新引入的类型 BigInt 和 Symbol。BigInt.prototype[Symbol.toStringTag]; // => “BigInt”Symbol.prototype[Symbol.toStringTag]; // => “Symbol”// 3. 四个集合(Collection)对象。Set.prototype[Symbol.toStringTag]; // => “Set”Map.prototype[Symbol.toStringTag]; // => “Map”WeakSet.prototype[Symbol.toStringTag]; // => “WeakSet”WeakMap.prototype[Symbol.toStringTag]; // => “WeakMap”// 4. 在不同的实现中,有些第三方对象也部署了此属性。// 比如在浏览器中:Window.prototype[Symbol.toStringTag]; // => “Window”HTMLElement.prototype[Symbol.toStringTag]; // => “HTMLElement”Blob.prototype[Symbol.toStringTag]; // => “Blob”// 5. 模块命名空间对象(Module Namespace Object)。// 新引入的模块命名空间对象(Module Namespace Object)也是部署了此属性的。import * as module from “./export.js”;module[Symbol.toStringTag]; // => “Moduel”// 6. 在 Node.js 中global[Symbol.toStringTag]; // => “global”
我们再来看一下 Object 以及其原型上的 toString 方法:
Object.toString(); // “function Object() { [native code] }”Object.prototype.toString(); // “[object Object]”var o = new Object();console.log(o.toString()); // 返回 [object Object]console.log(o.__proto__.toString()); // 返回 [object Object]console.log(o.__proto__.toString === Object.prototype.toString); // true
我们可以看出 Object 和它的原型链上各自有一个 toString 方法,Object 输出的是其函数体 “function Object() { [native code] }”,而 Object 原型上输出的是其类型 “[object Object]”。
数据类型 | 例子 | 输出 |
字符串 | “foo”.toString() | “foo” |
数字 | 1.toString() | Uncaught SyntaxError: Invalid or unexpected token |
布尔值 | true.toString() | “true” |
undefined | undefined.toString() | Uncaught TypeError: Cannot read property ‘toString’ of undefined |
null | null.toString() | Uncaught TypeError: Cannot read property ‘toString’ of null |
String | String.toString() | “function String() {[native code]}” |
Number | Number.toString() | “function Number() {[native code]}” |
Boolean | Boolean.toString() | “function Boolean() {[native code]}” |
Array | Array.toString() | “function Array() {[native code]}” |
Function | Function.toString() | “function Function() {[native code]}” |
Date | Date.toString() | “function Date() {[native code]}” |
RegExp | RegExp.toString() | “function RegExp() {[native code]}” |
Error | Error.toString() | “function Error() {[native code]}” |
Promise | Promise.toString() | “function Promise() {[native code]}” |
Object | Object.toString() | “function Object() {[native code]}” |
Math | Math.toString() | “[object Math]” |
Window | Window.toString() | “function Window() { [native code] }” |
window | window.toString() | “[object Window]” |
数据类型调用 toString() 方法的返回值,由此我们看出不同的数据类型都有其自身toString()方法
// Boolean 类型,tag 为 “Boolean”console.log(Object.prototype.toString.call(true)); // => “[object Boolean]”// Number 类型,tag 为 “Number”console.log(Object.prototype.toString.call(1)); // => “[object Boolean]”// String 类型,tag 为 “String”console.log(Object.prototype.toString.call(“”)); // => “[object String]”// Array 类型,tag 为 “String”console.log(Object.prototype.toString.call([])); // => “[object Array]”// Arguments 类型,tag 为 “Arguments”console.log(Object.prototype.toString.call((function() { return arguments;})())); // => “[object Arguments]”// Function 类型, tag 为 “Function”console.log(Object.prototype.toString.call(function(){})); // => “[object Function]”// Error 类型(包含子类型),tag 为 “Error”console.log(Object.prototype.toString.call(new Error())); // => “[object Error]”// RegExp 类型,tag 为 “RegExp”console.log(Object.prototype.toString.call(/d+/)); // => “[object RegExp]”// Date 类型,tag 为 “Date”console.log(Object.prototype.toString.call(new Date())); // => “[object Date]”// 其他类型,tag 为 “Object”console.log(Object.prototype.toString.call(new class {})); // => “[object Object]”// window 全局对象console.log(Object.prototype.toString.call(window); // => “[object Window]”)
在 JavaScript 中,所有类都继承于 Object,因此 toString 方法应该也被继承了,但由上述可见事实并不像我们想的那样,其实各数据类型使用 toString() 后的结果表现不一的原因在于:所有类在基础 Object 的时候,改写了 toString 方法。尽管如此,但 Object 原型上的方法是可以输出数据类型的,因此我们想判断数据类型时,也只能使用原型上的 toString 方法:Object.prototype.toString.call(object) 。
直接调用
toString(); // “[object Undefined]”(function(){ console.log(toString()); // [object Undefined]})();也就是说直接调用toString()方法,等价于Object.prototype.toString.call(); // “[object Undefined]”Object.prototype.toString.call(undefined); // “[object Undefined]”即:直接调用 toString() 方法这里不可以理解成为全局作用域调用 toString() 方法,即 window.toString()所以直接调用 toString() 应该就是变相的 undefined.toString() 方法(这里说的是相当于,实际 undefined 并没有方法,调用会报错)。
验证
// 定义一个数组var arr = [1, 2, 3]// 数组原型上是否具有 toString() 方法console.log(Array.prototype.hasOwnProperty(‘toString’)) //true// 数组直接使用自身的 toString() 方法console.log(arr.toString()) // ‘1,2,3’// delete操作符删除数组原型上的 toString()delete Array.prototype.toString// 删除后,数组原型上是否还具有 toString() 方法console.log(Array.prototype.hasOwnProperty(‘toString’)) //false// 删除后的数组再次使用 toString() 时,会向上层访问这个方法,即 Object 的 toString()console.log(arr.toString()) // ‘[object Array]’
当我们把 Array 自身的 toString() 方法删除之后,再次使用它时,由原型链它会向上查找这个方法,即 Object 的 toString(),也便将 Object 上的 toString() 方法作用在数组上,得出其数据类型 [object Array] 。
为什么需要call/apply
经常有人用 toString.call/apply(类型) 去代替 Object.prototype.toString.call/apply(类型) 使用,其实这样是不严谨的,容易导致一些问题,如下所示
function toString(){ console.log(“1”)}toString(); // 1toString.call({}); // 1toString.call([]); // 1
我们可以发现,当我们自定义了 toString() 方法时,直接调用 toString() 方法,就不会再默认调用 Object 类的 toString() 方法,而是会使用我们自定义的方法,这样可能得不到我们想要的结果,所以我们还是应当尽量使用 Object.prototype.toString.call/apply(类型)。
正因为 Object.prototype.toString() 本身允许被重写,像 Array、Boolean、Number 的 toString 就被重写过,所以需要调用 Object.prototype.toString.call(arg) 或 Object.prototype.toString.apply(arg) 或 Reflect.apply() 来判断 arg 的类型,call 将 arg 的上下文指向 Object,所以 arg 执行了 Object 的 toString() 方法。
至于 call,就是改变对象的 this 指向,当一个对象想调用另一个对象的方法,可以通过 call 或者 apply 改变其 this 指向,将其 this 指向拥有此方法的对象,就可以调用该方法了。
var x = { toString() { return “X”; },};x.toString(); // => “X”Object.prototype.toString.call(x); // => “[object Object]”Object.prototype.toString.apply(x); // => “[object Object]”Reflect.apply(Object.prototype.toString, x, []); // => “[object Object]”
判断原生JSON对象
var isNativeJSON = window.JSON && Object.prototype.toString.call(JSON);console.log(isNativeJSON); // 输出结果为 “[object JSON]” 说明JSON是原生的,否则不是;