这是一个根底构造问题,一个残缺的 JavaScript 包含:ECMAScript、文档对象模型(DOM)、浏览器对象模型(BOM),本章解说 ECMAScript
ECMAScript 规定这门语言由以下组成:
- 语法
- 变量和数据类型
- 关键字和保留字
- 操作符
- 语句
- 对象
也就是说,这六大模块组成了这门语言
这六大支柱中,像「语法」、「变量」和「数据类型」、「关键字」和「保留字」、「操作符」、「语句」都是极好了解的,只「对象」有点简单,下节会对其进行阐明,这里咱们来看看「数据类型」
数据类型
JavaScript 的数据类型分为「根本类型」和「援用类型」
根本类型在不同的书(教程中)叫法不同,它同样被叫做根本类型 / 值类型 / 原始值 / 原始类型。当然援用类型也有不一样的叫法,如对象类型 / 简单类型
根本类型蕴含 undefined、null、string、number、boolean、symbol(ES6 新增)、bigint(ES10 新增)
援用类型则是 object(一组属性的汇合)
特此说明:
在《JavaScript 高级程序设计第四版》中曾把数据类型演绎为驼峰模式的 Undefined、Null、Boolean、Number、String、Symbol、BigInt、Object,而在网站古代 JavaScript 教程、MDN 中则以小写的模式展现展现数据类型,笔者这里以网站为更大根据
根本类型和援用类型的区别
根本类型贮存在栈内存中
援用类型贮存在堆内存中
根本类型花销的内存小,所以拷贝值
援用类型花销的内存大,所以拷贝援用地址
// 案例 1
var string1 = 'foo';
var string2 = string1;
string1 = 'bar';
console.log(string1, string2); // 输入 bar, foo
// 案例 2
var object1 = {
name: 'johan',
age: 23,
};
var object2 = object1;
object1.name = 'elaine';
object2.age = 22;
console.log(object1, object2);
// 输入 {name: 'elaine', age: 23} {name: 'elaine', age: 23}
因为根本类型是寄存在栈内存中,string1 赋值给 string2 后,相当于开了一个独立空间给 string2,string1 和 string2 是齐全独立的两个个体,再给 string1 赋值什么,都与 string2 无关
而援用类型之所以叫援用类型(或叫简单类型 / 简单值),因为对象的地址是援用的,Object 能够始终嵌套,Function 也能够嵌套很多很多层(回调函数),Array 能够变花色的做出三维数组之类,总之,对象值能够无限大。值越大,就越占内存,如果一个很大的值随便复制,那对使用者来说就是劫难(人不知; 鬼不觉中就占了很大的内存)。所以它不可能随便复制,而是用指针的形式来将对象指向同一个地址(这里能够衍生到深拷贝)
当你创立一个 object1,就在堆内存中开拓一个内存。如果你赋值给 object2,其实就是把 object1 指向堆内存的地址复制给 object2,它们指向的是同一个地址。再在 object1 或者 object2 做批改的话,相当于在同一内存上做批改,无论 object1 批改还是 object2 批改都会扭转
var obj1 = {};
var obj2 = obj1;
obj1.name = 'elaine';
obj2.age = 23;
console.log(obj1); // {name: 'elaine', age: 22};
console.log(obj2); // {name: 'elaine', age: 22};
衍生思考:尽管说 JavaScript 的动态性很是不便,然而如果一些老手批改了对象类型的值而不告知,那么就成了劫难。因为动态性 + 全局作用域,所有变量命名就成了问题,所以个别的库都是用 IIFE、闭包等办法破局,再前面就有了模块化(ES module)的概念,其本质是为了解决变量命名、语言动态性的问题
光晓得 JavaScript 的数据类型不够,如何更精确的晓得每个数据类型
判断数据类型的形式有四种
- 应用 typeof
- 应用 instanceof
- 应用 constructor
- 应用 Object.prototype.toString.call()
类型判断
typeof 操作符
作用:返回正在应用的值的根本类型
// 根本类型 / 值类型 / 原始值 / 原始类型
var null1 = null;
var undefined1 = undefined;
var string1 = 'foo';
var number1 = Number('10');
var boolean1 = Boolean('true');
var symbol1 = Symbol('foo');
console.log(typeof null1); // object, 须要留神
console.log(typeof undefined1); // undefined
console.log(typeof string1); // string
console.log(typeof number1); // number
console.log(typeof boolean1); // boolean
console.log(typeof symbol1); // symbol
// 援用类型 / 简单值
var myString = new String('male');
var myNumber = new Number(23);
var myBoolean = new Boolean(false);
var myObject = new Object();
var myArray = new Array('foo', 'bar');
var myFunction = new Function('x', 'y', 'return x * y');
var myDate = new Date();
var myRegExp = new RegExp('\\bt[a-z]+\\b');
var myError = new Error('error');
console.log(typeof myString); // 输入 object
console.log(typeof myNumber); // 输入 object
console.log(typeof myBoolean); // 输入 object
console.log(typeof myObject); // 输入 object
console.log(typeof myArray); // 输入 object
console.log(typeof myFunction); // 输入 function 须要留神
console.log(typeof myDate); // 输入 object
console.log(typeof myRegExp); // 输入 object
console.log(typeof myError); // 输入 object
提醒:typeof xxx 和 typeof(xxx) 一个意思
通过 typeof
操作符能判断出应用的值的类型。须要留神判断援用类型时的问题
- null 类型会返回 object
- new Function,返回的是 function
轻易一说,这也是咱们看很多源码或者本人写代码时罕用 typeof 来判断 function 类型
if (typeof XXX === 'function') {// 如果 XXX 是 function 的话做什么操作}
PS:《JavaScript 启示录》里第一章第八节里说 RegExp() 的类型返回的是 function,然而笔者测验后发现并不是,笔者猜想因为老版本的浏览器对 RegExp 的判断为 funtion,而笔者用 Chrome 浏览器示意 RegExp 的类型为 object
instanceof 运算符
instanceof
运算符用于检测构造函数的 prototype 属性是否呈现在某个实例对象的原型链上
function People(name, age) {
this.name = name;
this.age = age;
}
const elaine = new People('elaine', 23);
console.log(elaine instanceof People);
console.log(elaine instanceof Object);
instanceof 运算符能让咱们找到它的爸爸是谁(谁制作了它,从生理上讲应该是妈妈是谁),以及它的祖宗十八代,这也是面试中常遇到的——instanceof 的原理是什么
constructor 构造方法
constructor
是一种用于创立和初始化 class 创立的对象的非凡办法。请留神,它是函数(办法)
就好比:
function sayHello() {console.log('hello');
}
不要因为单词生疏误以为它是属性
语法:
constructor([arguments]) {...}
这里多说一句,React 类组件是这样写的:
class HelloWorld extends React.Component {constructor(props) {super(props);
}
}
这里的意思很明确,HelloWorld
组件继承了 React.Component
组件,constructor(props)
意味着调用了父类的构造函数,并将 props 传递给 HelloWorld,应用 super
是因为在派生类中,必须调用 super
能力应用 this
,当然,这部分就关系到 class 的常识,在此不表
对于构造函数(constructor)须要晓得的是它是 Object 原型上的办法,即 Object.prototype.constructor,所有对象都会有 constructor 属性,它指向该对象的构造函数,对于继承后续文章会解说
说完题外话,咱们持续来看 constructor
是否测验数据类型吗
// 借 typeof 中的例子,间接打印看成果
// 根本类型
console.log(null1.constructor); // Cannot read properties of null (reading 'constructor')
console.log(undefined1.constructor); // Cannot read properties of null (reading 'constructor')
console.log(string1.constructor); // String() { [native code] }
console.log(number1.constructor); // Number() { [native code] }
console.log(boolean1.constructor); // Boolean() { [native code] }
console.log(symbol1.constructor); // Symbol() { [native code] }
// 援用类型
console.log(myString.constructor); // String() { [native code] }
console.log(myNumber.constructor); // Number() { [native code] }
console.log(myBoolean.constructor); // Boolean() { [native code] }
console.log(myObject.constructor); // Object() { [native code] }
console.log(myArray.constructor); // Array() { [native code] }
console.log(myFunction.constructor); // Function() { [native code] }
console.log(myDate.constructor); // Date() { [native code] }
console.log(myRegExp.constructor); // RegExp() { [native code] }
console.log(myError.constructor); // Error() { [native code] }
综上所述,constructor 对 underfined 和 null 有效(因为它们不是对象,不能从 Object.prototype 上继承 constructor 属性)
此外,constructor 的指针是能够扭转的(因为它就是个属性,以下例子属于属性赋值)
function Person() {}
function Student() {}
Student.prototype = new Person();
var student = new Student();
console.log(student.constructor); // Person() {}
具体咱们在原型一文中做具体介绍
Object.prototype.toString.call(source)
toString()
办法返回一个示意该对象的字符串
每个对象都有一个 toString()
办法(继承自 Object.prototype 老祖), 用它能真正做到对类型的检测
// 持续援用上述例子
// 根本类型
console.log(Object.prototype.toString.call(null1)); //[object Null]
console.log(Object.prototype.toString.call(undefined1)); //[object Undefined]
console.log(Object.prototype.toString.call(string1)); //[object String]
console.log(Object.prototype.toString.call(number1)); //[object Number]
console.log(Object.prototype.toString.call(boolean1)); //[object Boolean]
console.log(Object.prototype.toString.call(symbol1)); //[object Symbol]
// 援用类型
console.log(Object.prototype.toString.call(myString)); //[object String]
console.log(Object.prototype.toString.call(myNumber)); //[object Number]
console.log(Object.prototype.toString.call(myBoolean)); //[object Boolean]
console.log(Object.prototype.toString.call(myObject)); //[object Object]
console.log(Object.prototype.toString.call(myArray)); //[object Array]
console.log(Object.prototype.toString.call(myFunction)); //[object Function]
console.log(Object.prototype.toString.call(myDate)); //[object Date]
console.log(Object.prototype.toString.call(myRegExp)); //[object RegExp]
console.log(Object.prototype.toString.call(myError)); //[object Error]
咱们能够看到它返回 [object NativeConstructorName] 格局的字符串,它能清晰的判断咱们所须要的原生构造函数。同样,它的毛病是不能检测非原生构造函数
在 jquery 中的 $.type() 是外部的原理用的就是 Object.prototype.toString.call(),让咱们手写一个类型判断的小小工具库吧
function isType(source) {const target = Object.prototype.toString.call(source);
switch(target) {case "[object Null]":
return 'null';
case "[object Undefined]":
return 'undefined';
case "[object String]":
return 'string';
case "[object Number]":
return 'number';
case "[object Boolean]":
return 'boolean';
case "[object Object]":
return 'object';
case "[object Array]":
return 'array';
case "[object Function]":
return 'function';
case "[object Date]":
return 'date';
case "[object RegExp]";
return 'regexp';
case "[object Error]";
return 'error'
}
}
function getType(target) {return Object.prototype.toString.call(target);
}
这里你或者会感到纳闷,Object.prototype 的 constructor 不能调用 null、undefined,而 Object.prototype.toString 却能调用。这是为什么?
这里牵扯到隐式原型继承,即用对象字面量时,就曾经实现了继承,而 Object.prototype.toString 用的是原始办法,不信,你看
console.log(null1.toString()); // Cannot read properties of null (reading 'toString')
console.log(undefined1.toString()); // Cannot read properties of undefined (reading 'toString')
总结
这一节咱们讲了 JavaSript 由什么组成
从组成上分,它包含语法,变量和数据类型,关键字和保留字,操作符,语句,对象
其中数据类型包含根本类型(简略类型 / 值类型)和援用类型(简单类型)。根本类型有 number、string、boolean、null、undefined、symbol、bigInt,援用类型则是 object
那如何判断数据类型呢?笔者总结四种办法
名称 | 能检测 | 不能检测 |
---|---|---|
typeof | string、number、boolean、undefined 以及 function | null 以及除 function 外的对象,后果都为 object |
instanceof | 精确地判断简单援用数据类型 | 不能正确判断根底数据类型 |
constructor | string、number、boolean、array、object、function 以及 构造函数 | undefined、null。不平安,因为指向能够扭转 |
Object.prototype.toString() | 内置(原生)构造函数 | 自定义构造函数 |
咱们理解了根本类型、援用类型,它们的区别以及如何辨别它们,但根本类型是简略的,而援用类型尽管只有 object,但它却在 JavaScript 占了大部分知识点,只有把握 object,才算学会 JavaScript
下一篇,咱们讲讲 JavaScript 中的 KING—— 对象
系列文章
- 深刻了解 JavaScript- 开篇
- 深刻了解 JavaScript-JavaScript 是什么