编程技术文章分享与教程

网站首页 > 技术文章 正文

JavaScript中的数据类型判断

hmc789 2024-11-18 12:55:37 技术文章 2 ℃

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') // true
console.log(typeof 3.14 === 'number') // true
console.log(typeof (42) === 'number') // true
console.log(typeof Math.LN2 === 'number') // true
console.log(typeof Infinity === 'number') // true
console.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') // true
console.log(typeof 'bla' === 'string') // true
console.log(typeof `template literal` === 'string') // true
console.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') // true
console.log(typeof false === 'boolean') // true
console.log(typeof Boolean(1) === 'boolean') // Boolean() 会基于参数是真值还是虚值进行转换
console.log(typeof !!(1) === 'boolean') // true 两次调用 ! (逻辑非) 操作符相当于 Boolean()


// Symbols
console.log(typeof Symbol() === 'symbol') // true
console.log(typeof Symbol('foo') === 'symbol') // true
console.log(typeof Symbol.iterator === 'symbol') // true


// Undefined
console.log(typeof undefined === 'undefined') // true
console.log(typeof declaredButUndefinedVariable === 'undefined') // true
console.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') // true

console.log(typeof new Date() === 'object') // true
console.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') // true
console.log(typeof new Number(1) === 'object') // true
console.log(typeof new String('abc') === 'object') // true


// 函数
console.log(typeof function () { } === 'function') // true
console.log(typeof class C { } === 'function') // true
console.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); // number
console.log(typeof BigInt(Number.MAX_SAFE_INTEGER)); // bigint
console.log(typeof '0'); // string
console.log(typeof true); // boolean
console.log(typeof undefined); // undefined
console.log(typeof function () { }); // function
console.log(typeof Symbol); // function
console.log(typeof Symbol()); // symbol

console.log(typeof Date); // function
console.log(typeof Date()); // string
console.log(typeof new Date); // object
console.log(typeof new Date()); // object
console.log(typeof RegExp); // function
console.log(typeof RegExp()); // object
console.log(typeof new RegExp); // object
console.log(typeof new RegExp()); // object
console.log(typeof []); // object
console.log(typeof {}); // object
console.log(typeof null); // object

如果我们想判断一个对象的正确类型,可以考虑使用 instanceof,因为内部机制是通过 判断实例对象的 __proto__ 和生成该实例的构造函数的 prototype 是不是引用的同一个地址(也就是原型链的方式)来判断的。

错误

在 ECMAScript 2015 之前,typeof 总能保证对任何所给的操作数返回一个字符串。即便是没有声明的标识符,typeof 也能返回 'undefined'。使用 typeof 永远不会抛出错误。


但在加入了块级作用域的 letconst 之后,在其被声明之前对块中的 let 和 const 变量使用 typeof 会抛出一个 ReferenceError。块作用域变量在块的头部处于“暂存死区”,直至其被初始化,在这期间,访问变量将会引发错误。

typeof undeclaredVariable === 'undefined';

typeof newLetVariable; // ReferenceError
typeof newConstVariable; // ReferenceError
typeof newClass; // ReferenceError

let 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: true

console.log(auto instanceof Object);
// expected output: true
// 定义构造函数
function C(){}
function D(){}

var o = new C();

o instanceof C; // true,因为 Object.getPrototypeOf(o) === C.prototype

o instanceof D; // false,因为 D.prototype 不在 o 的原型链上

o instanceof Object; // true,因为 Object.prototype.isPrototypeOf(o) 返回 true
C.prototype instanceof Object // true,同上

C.prototype = {};
var o2 = new C();

o2 instanceof C; // true

o instanceof C; // false,C.prototype 指向了一个空对象,这个空对象不在 o 的原型链上.

D.prototype = new C(); // 继承
var o3 = new D();
o3 instanceof D; // true
o3 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); // true
console.log(c instanceof A); // true
console.log(A instanceof B); // false
console.log(B instanceof A); // false
console.log(B.__proto__ === A.prototype); // false
console.log(B.prototype.__proto__ === A.prototype); // true
console.log(c instanceof Object); // true c 属于
console.log(B instanceof Object); // true
console.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, 非对象实例,因此返回 false
console.log(myString  instanceof String) // 返回 true
console.log(newStr    instanceof String) // 返回 true
console.log(myString  instanceof Object) // 返回 true

console.log(myObj instanceof Object)     // 返回 true, 尽管原型没有定义
console.log(({})  instanceof Object)     // 返回 true, 同上
console.log(myNonObj instanceof Object)  // 返回 false, 一种创建非 Object 实例的对象的方法

console.log(myString instanceof Date)    // 返回 false

console.log(myDate instanceof Date)      // 返回 true
console.log(myDate instanceof Object)    // 返回 true
console.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)) // Number
console.log(getTypeof("kevin")) // String
console.log(getTypeof(new Date)) // Date
console.log(getTypeof([])) // Array
console.log(getTypeof({})) // Object
function Person() { }
console.log(getTypeof(new Person)) // Object
console.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.5
toString.call(undefined); // [object Undefined]
toString.call(null); // [object Null]


检测原理

Object.prototype.toString.call(obj) 类型检测的原理是什么?首先我们来看一下 toString() 方法:

var num = 1
console.log(num.toString())       // '1'

var str = 'kevin'
console.log(str.toString())       // 'kevin'

var bool = false
console.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 null

console.log(undefined.toString()  // Cannot read property 'toString' of undefined

console.log(window.toString())    // '[object Window]'

从以上示例可以知道 toString 是将数据转换为字符串(null 和 undefined 除外),并且各种类型的数据转换为字符串的方式又不一样。即若参数不为 nullundefined,则将参数转为对象,再作判断。对于原始类型,转为对象的方法即装箱。

转为对象后,取得该对象的 [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();        // 1
toString.call({}); // 1
toString.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是原生的,否则不是;




Tags:

标签列表
最新留言