JavaScript是知识按照ECMAScript标准设计和实现的,后文说的点整JavaScript语法其实是ES5的标准的实现。 先说说有哪些基础语法?知识 最基础语法有哪些? 基础语法几乎所有的语言差异不大,无非数据类型、点整操作符、知识控制语句、点整函数等,知识简单列举下。点整 5种基本数据类型 & 1种复杂的知识数据类型 JavaScript包含5种基本数据类型,分别是点整undefined / null / boolean / number / string,基本数据类型就这五种,知识没有其他的点整! JavaScript包含1种复杂的数据类型,就是知识Object类型,Object类型是点整所有其他对象的基类。 注意:JavaScript并不区分浮点数和整数,知识都是用number来表示。 前面提到的5种基本数据类型,以及这儿的服务器托管1种复杂数据类型,这就是数据类型的全部了! 基本操作符 这个是常识,知道怎么回事就好。 常用的操作符包括:算术操作符、关系操作符、布尔操作符、赋值操作符等。 控制语句 这就是我们常说的if-else之类的控制语句。 常用的并不多:if语句、switch语句、for语句、while语句、for-in语句。 函数 函数就是一小段逻辑的封装,理论上逻辑越独立越好。 JavaScript函数相对其他语言来说有很大不同。JavaScript函数既可以作为参数,也可以作为返回值。 此外JavaScript函数可以接受任意数量的参数,并且可以通过arguments对象来访问这些参数。 任何一门语言的基础语法都是亿华云计算相通的,除开一些细节差异,大致就是上面这些了:数据类型、操作符、控制语句、函数、模块等等。 接下来介绍稍微复杂的一些概念。 变量、作用域、内存问题 变量 JavaScript变量分为两种:基本类型和引用类型。其中基本类型就是前面提到的5种基本数据类型,引用类型就是前面提到的Object以及基于它的其他复杂数据类型。 一句话就是,基本类型在内存中是实际的值;而引用类型在内存中就是一个指针,指向一个对象,多个引用类型可能同时指向同一个对象。 那么,如何确定某个变量是哪种数据类型呢? 确定一个变量是哪种基本类型用typeof操作符。 确定一个变量是哪种引用类型用instanceof操作符。 这个别忘了! 作用域 变量是在某个特定的作用域中声明的,作用域决定了这些变量的生命周期,以及哪些代码可以访问其中的变量。 JavaScript作用域只包括全局作用域和函数作用域,并不包含块级作用域! 作用域是可以嵌套的,从而形成作用域链。由于作用域链的存在,可以让变量的查找向上追溯,即子函数可以访问父函数的作用域=>祖先函数的作用域=>直到全局作用域,这种函数我们也称为闭包,后文会介绍。 如下图所示,每个作用域能够访问到的变量以及嵌套的作用域可向上追溯。 作用域的概念看着简单,实际使用会有不少问题,遇到问题要细心分析。 内存问题 JavaScript引擎具有自动垃圾回收机制,不需要太关注内存分配和垃圾回收问题。这儿就不展开了! 引用类型 前面提过,Object是唯一的复杂数据类型,引用类型都是从Object类型上继承而来。 那问题来了,我们用的最多的函数是什么数据类型呢?答案是Function类型! 诶,好像发现了点什么东西?由于Function是引用类型,而JavaScript又可以往引用类型上加属性和方法。那么,函数也可以!这也是JavaScript函数强大和复杂的地方。也就是说:函数也可以拥有自定义方法和属性! 此外,JavaScript对前面提到的5种基本类型的其中3种也做了引用类型封装,分别是Boolean、Number、String,但其实使用不多,了解就行。 对了,在所有代码执行之前,作用域就内置了两个对象,分别是Global和Math,其中浏览器的Global就是window啦! 到此为止,JavaScript中基础的概念都差不多介绍了,其中函数和作用域相对来说复杂一些,其他的都比较浅显。 接下来,我会介绍介绍JavaScript中一些稍微复杂一些的概念:面向对象。 面向对象编程 JavaScript本身并没有类和接口的概念了,面向对象都是基于原型实现的。 为了简单,我们只分析面向对象的两个问题: 定义一个类 不扯其他的,直接告诉你。我们使用构造函数+原型的方式来定义一个类。 使用构造函数创建自定义类型,然后使用new操作符来创建类的实例,但是构造函数上的方法和属性在每个示例上都存在,不能共享,于是我们引入原型来实现方法和属性的共享。 ***,我们将需要共享的方法和属性定义在原型上,把专属于实例的方法和属性放到构造函数中。到这儿,我们就通过构造函数+原型的方式定义了一个类。 实现继承 前文讲了如何定义一个类,那么我们定义一个父类,一个子类。 如何让子类继承父类呢?不扯别的,直接告诉你。JavaScript通过原型链来实现继承! 如何构建原型链呢?将子类实例赋值给父类构造函数的原型即可。好绕,但是千万得记住了! 构建原型链之后,子类就可以访问父类的所有属性和方法! 面向对象的知识可以用一本书来写,这儿只是简单的介绍下最基础最常用的概念。 函数表达式 JavaScript中有两种定义函数的方式:函数声明和函数表达式。 使用函数表达式无须对函数命名,从而实现动态编程,也即匿名函数。有了匿名函数,JavaScript函数有了更强大的用处。 递归 递归是一种很常见的算法,经典例子就是斐波拉契数列。也不扯其他的,直接说递归的***实践,上代码: 递归就是这样,好多人还在使用arguments.callee的方式,改回函数表达式的方式吧,这才是***实践。 啰嗦一句,好多人觉得递归难写,其实你将其分为两个步骤就会清晰很多了。 按这个模式,找几个经典的递归练练手,就熟悉了。 闭包 很多人经常觉得闭包很复杂,很容易掉到坑里,其实不然。 那么闭包是什么呢?如果一个函数可以访问另一个函数作用域中的变量,那么前者就是闭包。由于JavaScript函数可以返回函数,自然,创建闭包的常用方式就是在一个函数内部创建另一个函数! 这并没有什么神奇的,在父函数中定义子函数就可以创建闭包,而子函数可以访问父函数的作用域。 我们通常是因为被闭包坑了,才会被闭包吓到,尤其是面试题里一堆闭包。 闭包的定义前面提了,如何创建闭包也说了,那么我们说说闭包的缺陷以及如何解决? 综上,闭包本身不是什么复杂的机制,就是子函数可以访问父函数的作用域。 而由于JavaScript函数的特殊性,我们可以返回函数,如果我们将作为闭包的函数返回,那么该函数引用的父函数变量是父函数运行结束之后的状态,而不是运行时的状态,这便是闭包***的坑。而为了解决这个坑,我们常用的方式就是让函数表达式自执行。 此外,由于闭包引用了祖先函数的作用域,所以滥用闭包会有内存问题。 好像把闭包说得一无是处,那么闭包有什么用处呢? 主要是封装吧... 封装 闭包可以封装私有变量或者封装块级作用域。 封装块级作用域 JavaScript并没有块级作用域的概念,只有全局作用域和函数作用域,那么如果想要创建块级作用域的话,我们可以通过闭包来模拟。 创建并立即调用一个函数,就可以封装一个块级作用域。该函数可以立即执行其中的代码,内部变量执行结束就会被立即销毁。 封装私有变量 JavaScript也没有私有变量的概念,我们也可以使用闭包来实现公有方法,通过隐藏变量暴露方法的方式来实现封装私有变量。 总结说点啥? 这差不多就是JavaScript的一些基础语法和稍微高级一些的用法,其实所谓的高级,都是JavaScript“不太成熟”的表现,尤其是面向对象,出于工程化的需要但是JavaScript本身并不***支持。好在ES6***标准解决了很多问题,结合Babel用起来也不用太考虑兼容性问题,如果你是新手的话,建议你直接去撸ES6+Babel吧。 JavaScript作为一门动态语言,和其他语言有较大的差异,这也造成很多人学习JavaScript时会觉得难学。但你现在看看前文,虽然是一个简略的总结,但JavaScript主要的内容就这些了,所以不要被自己吓到了。 再补一句,如果你是新手的话,建议你直接去撸ES6+Babel吧。变量内存分配
作用域链
原型
原型链继承