这是一个基础设施的问题。一个完整的JavaScript包括:ECMAScript、文档对象模型(DOM)、浏览器对象模型(BOM)。本章讲解ECMAScriptECMAScript指定语言它由以下部分组成:语法变量和数据类型关键字和保留字运算符语句对象也就是说,这六个模块构成了语言的六大支柱,如“语法”、“变量”"和"数据类型"、"关键字"和"保留字"、"运算符"、"语句"都很好理解,只有"对象"有点复杂,下一节再解释。这里我们来看看“数据类型”数据类型JavaScript数据类型分为“基本类型”和“引用类型”。基本类型在不同的书籍(教程)中的称呼不同,也称为基本类型/值类型/原始值/原始类型。当然,引用类型有不同的名称。例如对象类型/复杂类型的基本类型有undefined、null、string、number、boolean、symbol(ES6新增)、bigint(ES10新增)。引用类型为object(一组属性的集合)特此说明:在《JavaScript 高级程序设计第四版》中,数据类型归纳为Undefined、Null、Boolean、Number、String、Symbol、BigInt、驼峰式的Object,而在网站现代JavaScript教程和MDN,它以小写形式显示数据类型。笔者这里以网站作为区分基本型和参考型的依据。基本类型存储在栈内存中。引用类型存储在堆内存中。基本类型的内存开销小,所以复制值引用类型的开销是内存大,所以复制引用地址//case1varstring1='foo';var字符串2=字符串1;string1='酒吧';控制台日志(字符串1,字符串2);//输出bar,foo//case2varobject1={name:'johan',age:23,};varobject2=object1;object1.name='elaine';object2.age=22;console.log(object1,object2);//output{name:'elaine',age:22}{name:'elaine',age:22}因为基本类型存放在栈内存中,string1赋给string2后,等价给string2开一个独立的空间,string1和string2是两个完全独立的个体,给string1赋值和string2没有任何关系。引用类型之所以称为引用类型(或复杂类型/复杂值),是因为引用了对象的地址。Object总是可以嵌套的,Function也可以嵌套。很多很多层(回调函数),Array可以改成三维数组等等,总之对象的值可以无限大。值越大,占用的内存越多。如果随意复制一个很大的值,对用户来说将是一场灾难(在不知不觉中占用了大量内存)。所以不能随意复制,而是用指针将对象指向同一个地址(复制的秘密可以从这里推导出来)。当你创建一个object1时,你在堆内存中开辟了一块内存。如果给object2赋值,实际上是把object1指向堆内存的地址复制给object2,而且它们指向同一个地址。如果修改object1或者object2,就相当于修改了同一块内存,无论object1修改还是object2修改都会改变varobj1={};varobj2=obj1;obj1.name='elaine';obj2.age=23;控制台日志(obj1);//{姓名:'elaine',年龄:22};控制台日志(obj2);//{姓名:'elaine',年龄:22};衍生思维:虽然JavaScriptDynamism很方便,但是如果一些新手在不告诉它的情况下修改了对象类型的值,那将是一场灾难。因为dynamics+globalscope,给所有变量命名就成了问题,所以一般的库用IIFE、闭包等方法来打破这种局面,然后就有了模块化(ES模块)的概念,本质上就是解决变量的问题命名和语言动力学只知道JavaScript的数据类型是不够的,如何更准确的知道每种数据类型判断数据类型有四种方法使用typeof使用instanceof使用constructor使用Object.prototype.toString.call()type确定typeof运算符的作用:返回被使用值的基本类型//基本类型/值类型/原始值/原始类型varnull1=null;varundefined1=undefined;varstring1='foo';varnumber1=Number('10');varboolean1=Boolean('true');varsymbol1=Symbol('foo');console.log(typeofnull1);//对象,需要注意console.log(typeofundefined1);//未定义控制台。日志(字符串类型1);//stringconsole.log(typeofnumber1);//numberconsole.log(typeofboolean1);//booleanconsole.log(typeofsymbol1);//symbol//引用类型/复数值varmyString=newString('male');varmyNumber=newNumber(23);varmyBoolean=newBoolean(false);varmyObject=newObject();varmyArray=newArray('foo','bar');varmyFunction=newFunction('x','y','returnx*y');varmyDate=newDate();varmyRegExp=newRegExp('\\bt[a-z]+\\b');var我的Error=newError('错误');console.log(typeofmyString);//输出对象console.log(typeofmyNumber);//输出对象console.log(typeofmyBoolean);//输出对象console.log(typeofmyObject);//输出对象console.log(typeofmyArray);//输出对象console.log(typeofmyFunction);//输出函数需要注意console.log(typeofmyDate);//输出对象console.log(typeofmyRegExp);//输出对象console.log(typeofmyError);//输出对象提示:typeofxxx和typeof(xxx)表示使用的value的类型可以通过typeof运算符来判断判断引用类型时需要注意的问题。null类型会返回objectnew函数,返回的函数随便说。这也是为什么我们在大量阅读源代码或者自己写代码的时候,经常使用typeof来判断函数类型的原因。if(typeofXXX==='function'){//如果XXX是一个函数怎么办}PS:在《JavaScript 启示录》,第一章第八节,说RegExp()的类型返回一个函数,但是笔者发现不是,笔者猜测是因为旧版本的浏览器将RegExp判断为funtion,笔者使用Chrome浏览器说明RegExp的类型是objectinstanceof运算符,instanceof运算符用于检测构造函数的原型属性是否出现在实例对象的原型链上functionPeople(name,age){this.name=name;this.age=age;}constelaine=newPeople('elaine',23);console.log(elaineinstanceofPeople);console.log(elaineinstanceofObject);instanceofoperation这个符号可以让我们找出它的父亲是谁是(谁造的,从生理上讲应该是它的妈妈是谁),还有它的十八代祖宗,这也是面试中经常遇到的——instanceofconstructor的原理是什么constructor构造函数是一种特殊的创建和创建方法初始化类创建的对象。请注意,它是一个函数(方法),例如:functionsayHello(){console.log('hello');}不要将它误认为是属性语法,因为这个词不熟悉:constructor([arguments]){。..}这里还有一点,React类组件是这样写的:classHelloWorldextendsReact.Component{constructor(props){super(props);}}这里的意思很明确,HelloWorld组件继承了React.Component组件,constructor(props)表示调用父类的构造函数,将props传递给HelloWorld。使用super是因为在派生类中,必须调用super才能使用this。当然这部分和类的知识相关,这里就不展示结构了。函数(构造函数)需要知道的是它是Object原型上的方法,即Object.prototype.constructor。所有的对象都会有一个constructor属性,它指向对象的构造函数。后续关于继承的文章会跑题解释。继续看构造函数是否可以检查数据类型//使用typeof中的例子直接打印看效果//Basictypeconsole.log(null1.constructor);//无法读取null的属性(读取“构造函数”)控制台。日志(undefined1.constructor);//无法读取null的属性(读取'constructor')console.log(string1.constructor);//String(){[本地代码]}console.log(number1.constructor);//Number(){[nativecode]}console.log(boolean1.constructor);//Boolean(){[nativecode]}console.日志(symbol1.constructor);//Symbol(){[nativecode]}//引用类型console.log(myString.constructor);//String(){[本地代码]}console.log(myNumber.constructor);//Number(){[本地代码]}console.log(myBoolean.constructor);//Boolean(){[本地代码]}console.log(myObject.constructor);//Object(){[本地代码]}console.log(myArray.constructor);//Array(){[本地代码]}console.log(myFunction.constructor);//Function(){[本地代码]}console.log(myDate.constructor);//Date(){[本地代码]}console.log(myRegExp.constructor);//RegExp(){[本地代码]}console.log(myError.constructor);//Error(){[nativecode]}综上所述,构造函数对undefined和null无效(因为它们不是对象,不能继承Object.prototype的constructor属性)另外,构造函数的指针可以是改变了(因为是属性,下面的例子属于属性赋值)functionPerson(){}functionStudent(){}Student.prototype=newPerson();varstudent=newStudent();console.log(student.constructor);//Person(){}具体我们在prototype这篇文章中详细介绍了Object.prototype.toString.call(source)toString()方法返回表示对象的字符串。每个对象都有一个toString()方法(继承自Object.prototype的祖先),可以真正检测出类型//继续引用上面的例子//基本类型console.log(Object.prototype.toString.call(null1));//[objectNull]console.log(Object.prototype.toString.call(undefined1));//[对象未定义]console.log(Object.prototype.toString.call(string1));//[对象字符串]console.log(Object.prototype.toString.call(number1));//[对象编号]console.log(Object.prototype.toString.call(boolean1));//[对象布尔值]console.log(Object.prototype.toString.call(symbol1));//[objectSymbol]//引用类型console.log(Object.prototype.toString.call(myString));//[对象字符串]console.log(Object.prototype.toString.call(myNumber));//[对象编号]console.log(Object.prototype.toString.call(myBoolean));//[对象布尔值]console.log(Object.prototype.toString.call(myObject));//[objectObject]console.log(Object.prototype.toString.call(myArray));//[对象数组]console.log(Object.prototype.toString.call(my功能));//[对象函数]console.log(Object.prototype.toString.call(myDate));//[对象日期]console.log(Object.prototype.toString.call(myRegExp));//[objectRegExp]console.log(Object.prototype.toString.call(myError));//[objectError]我们可以看到它返回了一个字符串,格式为[objectNativeConstructorName],可以明确的判断我们需要什么nativeconstructor同样,它的缺点是无法检测非本地构造函数。$.type()在jquery中是一个内部原则。使用Object.prototype.toString.call()。让我们手写一个类型判断的小工具库。functionisType(source){consttarget=Object.prototype.toString.call(source);switch(target){case"[objectNull]":return'null';case“[objectUndefined]”:返回“undefined”case“[objectString]”:返回“string”;case"[objectNumber]":return'number';case"[objectBoolean]":返回'boolean';case"[objectObject]":return'object';case"[objectArray]":return'array';case"[objectFunction]":返回'function';案例“[对象日期]”:返回“日期”;案例“[对象正则表达式]”;返回“正则表达式”;案例“[对象错误]”;返回'错误r'}}functiongetType(target){returnObject.prototype.toString.call(target);}在这里你可能会感到疑惑,Object.prototype的构造函数不能调用null和undefined,而Object.prototype.toString却可以调用why就是它?这里涉及到隐式原型继承,即在使用对象字面量时,已经实现了继承,Object.prototype.toString使用的是原来的方法。如果你不相信我,看看console.log(null1.toString());//无法读取null的属性(读取'toString')console.log(undefined1.toString());//Cannotreadpropertiesofundefined(reading'toString')总结在这一节中,我们从组成上讲了JavaSript由什么组成,包括语法、变量和数据类型、关键字和保留字、运算符、语句、对象、其中数据类型包括基本类型(简单类型/值类型)和引用类型(复杂类型)。基本类型有number、string、boolean、null、undefined、symbol、bigInt,引用类型为object。如何判断数据类型?作者总结了四个方法名可以检测但是不能检测typeofstring,number,boolean,undefined和functionnull以及function以外的对象,结果objectinstanceof可以准确判断复杂的引用数据类型不能正确判断基本数据类型constructorstring,number,boolean、数组、对象、函数和构造函数未定义,为空。不安全,因为指针可以改变Object.prototype.toString()内置(原生)构造函数自定义构造函数我们了解了原始类型、引用类型、它们的区别以及如何区分它们,但是原始类型很简单,而引用类型虽然是onlyobject,它占据了JavaScript中大部分的知识点。只有掌握了object,才能学习JavaScript。下一篇我们来聊聊JavaScript中的KING——对象系列文章深入理解JavaScript——深入理解JavaScript的开端——什么是JavaScript深入理解JavaScript——什么是JavaScript由什么组成?深入理解JavaScript——一切皆对象JavaScript——复制的秘密深入理解JavaScript——Prototype深入理解JavaScript——继承深入理解JavaScript——JavaScript中的始祖深入理解JavaScript——instanceof——寻祖深入理解JavaScript——Function深入理解JavaScript——function深入理解JavaScript——this关键字深入理解JavaScript——call,apply,andbind三大将深入理解JavaScript——立即执行函数(IIFE)深入理解JavaScript——词法环境深入理解JavaScript——执行上下文和调用栈深入理解JavaScript-ScopeVSExecutionContext深入理解JavaScript——闭包深入理解JavaScript——防抖与节流深入理解JavaScript——函数式编程g深入理解JavaScript——垃圾回收机制深入理解JavaScript——数组深入理解JavaScript——循环来这里深入理解JavaScript——字符串
