javascript 基本特性
JavaScript是按照ECMAScript标准设计和实现的,后文说的JavaScript语法其实是ES5的标准的实现。
先说说有哪些基础语法?
01-
最基础语法有哪些?
基础语法几乎所有的语言差异不大,无非数据类型、操作符、控制语句、函数等,简单列举下。
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对象来访问这些参数。
任何一门语言的基础语法都是相通的,除开一些细节差异,大致就是上面这些了:数据类型、操作符、控制语句、函数、模块等等。
接下来介绍稍微复杂的一些概念。
02-
变量、作用域、内存问题
变量
JavaScript变量分为两种:基本类型和引用类型。其中基本类型就是前面提到的5种基本数据类型,引用类型就是前面提到的Object以及基于它的其他复杂数据类型。
基本类型:在内存中占据实际大小的空间,赋值的时候,会在内存中创建一份新的副本。保存在栈内存中。
引用类型:指向对象的指针而不是对象本身,赋值的时候,只是创建了一个新的指针指向对象。保存在堆内存中。
变量内存分配
一句话就是,基本类型在内存中是实际的值;而引用类型在内存中就是一个指针,指向一个对象,多个引用类型可能同时指向同一个对象。
那么,如何确定某个变量是哪种数据类型呢?
确定一个变量是哪种基本类型用typeof操作符。
确定一个变量是哪种引用类型用instanceof操作符。
这个别忘了!
作用域
变量是在某个特定的作用域中声明的,作用域决定了这些变量的生命周期,以及哪些代码可以访问其中的变量。
JavaScript作用域只包括全局作用域和函数作用域,并不包含块级作用域!
作用域是可以嵌套的,从而形成作用域链。由于作用域链的存在,可以让变量的查找向上追溯,即子函数可以访问父函数的作用域=>祖先函数的作用域=>直到全局作用域,这种函数我们也称为闭包,后文会介绍。
var color = "blue";
function changeColor() {
var anotherColor = "red";
function swapColors() {
var tempColor = anotherColor;
anotherColor = color;
color = tempColor;
// 这里可以访问color、anotherColor、tempColor
}
// 这里可以访问color、anotherColor,但不能访问tempColor
swapColors();
}
// 这里只能访问color、changeColor();
如下图所示,每个作用域能够访问到的变量以及嵌套的作用域可向上追溯。
作用域链
作用域的概念看着简单,实际使用会有不少问题,遇到问题要细心分析。
内存问题
JavaScript引擎具有自动垃圾回收机制,不需要太关注内存分配和垃圾回收问题。这儿就不展开了!
03-
引用类型
前面提过,Object是唯一的复杂数据类型,引用类型都是从Object类型上继承而来。
Array:数组类型
Date:日期类型
RegExp:正则表达式类型,这个多学学有好处!
等等...
那问题来了,我们用的最多的函数是什么数据类型呢?答案是Function类型!
诶,好像发现了点什么东西?由于Function是引用类型,而JavaScript又可以往引用类型上加属性和方法。那么,函数也可以!这也是JavaScript函数强大和复杂的地方。也就是说:函数也可以拥有自定义方法和属性!
此外,JavaScript对前面提到的5种基本类型的其中3种也做了引用类型封装,分别是Boolean、Number、String,但其实使用不多,了解就行。
对了,在所有代码执行之前,作用域就内置了两个对象,分别是Global和Math,其中浏览器的Global就是window啦!
到此为止,JavaScript中基础的概念都差不多介绍了,其中函数和作用域相对来说复杂一些,其他的都比较浅显。
接下来,我会介绍介绍JavaScript中一些稍微复杂一些的概念:面向对象。
03-
面向对象编程
JavaScript本身并没有类和接口的概念了,面向对象都是基于原型实现的。
为了简单,我们只分析面向对象的两个问题:
如何定义一个类?
如何实现类的继承
定义一个类
不扯其他的,直接告诉你。我们使用构造函数+原型的方式来定义一个类。
使用构造函数创建自定义类型,然后使用new操作符来创建类的实例,但是构造函数上的方法和属性在每个示例上都存在,不能共享,于是我们引入原型来实现方法和属性的共享。
原型
最后,我们将需要共享的方法和属性定义在原型上,把专属于实例的方法和属性放到构造函数中。到这儿,我们就通过构造函数+原型的方式定义了一个类。
// 构造函数
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.friends = ["Shelby", "Court"];
}
// 原型
Person.prototype = {
constructor: Person,
sayName: function() {
return this.name;
}
}
// 实例化
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
person1.friends.push("Van");
alert(person1.friends); //输出"Shelby,Count,Van"
alert(person2.friends); //输出"Shelby,Count"
alert(person1.friends === person2.friends); //输出false
alert(person1.sayName === person2.sayName); //输出true
实现继承
前文讲了如何定义一个类,那么我们定义一个父类,一个子类。
如何让子类继承父类呢?不扯别的,直接告诉你。JavaScript通过原型链来实现继承!
如何构建原型链呢?将父类实例赋值给子类构造函数的原型即可。好绕,但是千万得记住了!
原型链继承
构建原型链之后,子类就可以访问父类的所有属性和方法!
// 父类
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function() {
return this.property;
};
// 子类
function SubType() {
this.subproperty = false;
}
//子类继承父类
SubType.prototype = new SuperType();
//给子类添加新方法
SubType.prototype.getSubValue = function() {
return this.subproperty;
};
//重写父类的方法
SubType.prototype.getSuperValue = function() {
return false;
};
// 实例化
var instance = new SubType();
console.log(instance.getSuperValue()); //输出false
面向对象的知识可以用一本书来写,这儿只是简单的介绍下最基础最常用的概念。
03-
函数表达式
JavaScript中有两种定义函数的方式:函数声明和函数表达式。使用函数表达式无须对函数命名,从而实现动态编程,也即匿名函数。有了匿名函数,JavaScript函数有了更强大的用处。
递归
递归是一种很常见的算法,经典例子就是阶乘。也不扯其他的,直接说递归的最佳实践,上代码:
// 最佳实践,函数表达式
var factorial = (function f(num) {
if (num <= 1) {
return 1;
} else {
return num * f(num - 1);
}
});
// 缺点:
// factorial存在被修改的可能
// 导致 return num * factorial(num - 1) 报错
function factorial(num) {
if (num <= 1) {
return 1;
} else {
return num * factorial(num - 1);
}
}
// 缺点:
// arguments.callee,规范已经不推荐使用
function factorial(num) {
if (num <= 1) {
return 1;
} else {
return num * arguments.callee(num - 1);
}
}
递归就是这样,好多人还在使用arguments.callee的方式,改回函数表达式的方式吧,这才是最佳实践。
啰嗦一句,好多人觉得递归难写,其实你将其分为两个步骤就会清晰很多了。
边界条件,通常是if-else。
递归调用。
按这个模式,找几个经典的递归练练手,就熟悉了。
闭包
很多人经常觉得闭包很复杂,很容易掉到坑里,其实不然。
那么闭包是什么呢?如果一个函数可以访问另一个函数作用域中的变量,那么前者就是闭包。由于JavaScript函数可以返回函数,自然,创建闭包的常用方式就是在一个函数内部创建另一个函数!
这并没有什么神奇的,在父函数中定义子函数就可以创建闭包,而子函数可以访问父函数的作用域。
我们通常是因为被闭包坑了,才会被闭包吓到,尤其是面试题里一堆闭包。
闭包的定义前面提了,如何创建闭包也说了,那么我们说说闭包的缺陷以及如何解决?
/* 我们通过subFuncs返回函数数组,然后分别调用执行 */
// 返回函数的数组subFuncs,而这些函数对superFunc的变量有引用
// 这就是一个典型的闭包
// 那么有什么问题呢?
// 当我们回头执行subFuncs中的函数的时候,我们得到的i其实一直都是10,为什么?
// 因为当我们返回subFuncs之后,superFunc中的i=10
// 所以当执行subFuncs中的函数的时候,输出i都为10。
//
// 以上,就是闭包最大的坑,一句话理解就是:
// 子函数对父函数变量的引用,是父函数运行结束之后的变量的状态
function superFunc() {
var subFuncs = new Array();
for (var i = 0; i < 10; i++) {
subFuncs[i] = function() {
return i;
};
}
return subFuncs;
}
// 那么,如何解决上诉的闭包坑呢?
// 其实原理很简单,既然闭包坑的本质是:子函数对父函数变量的引用,是父函数运行结束之后的变量的状态
// 那么我们解决这个问题的方式就是:子函数对父函数变量的引用,使用运行时的状态
// 如何做呢?
// 在函数表达式的基础上,加上自执行即可。
function superFunc() {
var subFuncs = new Array();
for (var i = 0; i < 10; i++) {
subFuncs[i] = function(num) {
return function() {
return num;
};
}(i);
}
return subFuncs;
}
综上,闭包本身不是什么复杂的机制,就是子函数可以访问父函数的作用域。
而由于JavaScript函数的特殊性,我们可以返回函数,如果我们将作为闭包的函数返回,那么该函数引用的父函数变量是父函数运行结束之后的状态,而不是运行时的状态,这便是闭包最大的坑。而为了解决这个坑,我们常用的方式就是让函数表达式自执行。
此外,由于闭包引用了祖先函数的作用域,所以滥用闭包会有内存问题。
好像把闭包说得一无是处,那么闭包有什么用处呢?
主要是封装吧...
封装
闭包可以封装私有变量或者封装块级作用域。
➙ 封装块级作用域
JavaScript并没有块级作用域的概念,只有全局作用域和函数作用域,那么如果想要创建块级作用域的话,我们可以通过闭包来模拟。
创建并立即调用一个函数,就可以封装一个块级作用域。该函数可以立即执行其中的代码,内部变量执行结束就会被立即销毁。
function outputNumbers(count) {
// 在函数作用域下,利用闭包封装块级作用域
// 这样的话,i在外部不可用,便有了类似块级作用域
(function() {
for (var i = 0; i < count; i++) {
alert(i);
}
})();
alert(i); //导致一个错误!
}
// 在全局作用域下,利用闭包封装块级作用域
// 这样的话,代码块不会对全局作用域造成污染
(function() {
var now = new Date();
if (now.getMonth() == 0 && now.getDate() == 1) {
alert("Happy new year!");
}
})();
// 是的,封装块级作用域的核心就是这个:函数表达式 + 自执行!
(function() {
//这里是块级作用域
})();
➙ 封装私有变量
JavaScript也没有私有变量的概念,我们也可以使用闭包来实现公有方法,通过隐藏变量暴露方法的方式来实现封装私有变量。
(function() {
//私有变量和私有函数
var privateVariable = 10;
function privateFunction() {
return false;
}
//构造函数
MyObject = function() {};
//公有/特权方法
MyObject.prototype.publicMethod = function() {
privateVariable++;
return privateFunction();
};
})();
03-
总结说点啥?
这差不多就是JavaScript的一些基础语法和稍微高级一些的用法,其实所谓的高级,都是JavaScript“不太成熟”的表现,尤其是面向对象,出于工程化的需要但是JavaScript本身并不完美支持。好在ES6最新标准解决了很多问题,结合Babel用起来也不用太考虑兼容性问题,如果你是新手的话,建议你直接去撸ES6+Babel吧。
JavaScript的基础主要包括:5中基本数据类型、1种复杂的数据类型、操作符、控制语句、函数等。
了解基本的语法后,你还需要学习学习JavaScript的变量、作用域、作用域链。
常见的引用类型可以边查边用。作为过来人,建议多学学正则,对你的代码功底会有较大的提升。
面向对象编程的部分外面有很多种方式,你只需要记住使用构造函数+原型去定义一个类,使用原型链去实现继承即可。更多的扩展,去翻翻书吧。
函数表达式引出了几个比较好玩的东西:递归、闭包、封装。记住递归的最佳实践、闭包的定义及缺陷、闭包的适用场景。
JavaScript作为一门动态语言,和其他语言有较大的差异,这也造成很多人学习JavaScript时会觉得难学。但你现在看看前文,虽然是一个简略的总结,但JavaScript主要的内容就这些了,所以不要被自己吓到了。
转自:http://mp.weixin.qq.com/s/4Ty2LbQ_QIIQ8v2Ofr7yGw
javascript 基本特性的更多相关文章
- Javascript面向对象特性实现封装、继承、接口详细案例——进级高手篇
Javascript面向对象特性实现(封装.继承.接口) Javascript作为弱类型语言,和Java.php等服务端脚本语言相比,拥有极强的灵活性.对于小型的web需求,在编写javascript ...
- javascript高级特性
01_javascript相关内容02_函数_Arguments对象03_函数_变量的作用域04_函数_特殊函数05_闭包_作用域链&闭包06_闭包_循环中的闭包07_对象_定义普通对象08_ ...
- javascript高级特性(面向对象)
javascript高级特性(面向对象): * 面向对象: * 面向对象和面向过程的区别: * 面向对象:人就是对象,年龄\性别就是属性,出生\上学\结婚就是方法. * 面向过程:人出生.上学.工作. ...
- Javascript面向对象特性实现封装、继承、接口详细案例
Javascript面向对象特性实现(封装.继承.接口) Javascript作为弱类型语言,和Java.php等服务端脚本语言相比,拥有极强的灵活性.对于小型的web需求,在编写javascript ...
- JavaScript高级特性-数组
1. JavaScript中的数组 在C++.Java中,数组是一种高效的数据结构,随机访问性能特别好,但是局限性也特别明显,就是数组中存放的数据必须是同一类型的,而在JavaScript中,数组中的 ...
- JavaScript高级特性-创建对象的九种方式
1. 对象字面量 通过这种方式创建对象极为简单,将属性名用引号括起来,再将属性名和属性值之间以冒号分隔,各属性名值对之后用逗号隔开,最后一个属性不用逗号隔开,所有的属性名值对用大括号括起来,像这样: ...
- JavaScript this特性,静态方法 和实例方法,prototype
<script type="text/javascript"> function logs(str) { document.write(str + "< ...
- ES6:JavaScript 新特性
我相信,在ECMAScript.next到来的时候,我们现在每天都在写的JavaScript代码将会发生巨大的变化.接下来的一年将会是令JavaScript开发者们兴奋的一年,越来越多的特性提案将被最 ...
- 你从未听说过的 JavaScript 早期特性
最近这些年在对 JavaScript 进行考古时,发现网景时代的 JavaScipt 实现,存在一些鲜为人知的特性,我从中挑选几个有趣的说一下. Object.prototype.eval() 方法 ...
- JavaScript高级特性-实现继承的七种方式
声明和约定: 在C++和Java中,我们可以通过关键字class来声明一个类,在JavaScript中没有这个关键字,但我们知道可以通过new一个function创建对象,这个function类似C+ ...
随机推荐
- 游戏AI(二)—行为树优化之
上一篇我们讲到了AI架构之一的行为树,本篇文章和下一篇文章我们将对行为树进行优化,在本篇文章中我们讲到的是内存优化 问题 上一篇中我们设计的行为树由于直接采用new进行动态内存分配,没有自己进行管理. ...
- C# 函数式编程 —— 使用 Lambda 表达式编写递归函数
最近看了赵姐夫的这篇博客http://blog.zhaojie.me/2009/08/recursive-lambda-expressions.html,主要讲的是如何使用 Lambda 编写递归函数 ...
- mysql中对字符集和校对规则的认识
字符集:指符号和字符编码的集合.校对规则:比较字符编码的方式.GBK2312:主要包括简体中文字符及常用符号,对于中文字符采用双字节编码的格式,也就是说一个汉字字符在存储占两个字节.GBK:包括有中. ...
- 深入理解计算机系统_3e 第八章家庭作业 CS:APP3e chapter 8 homework
8.9 关于并行的定义我之前写过一篇文章,参考: 并发与并行的区别 The differences between Concurrency and Parallel +---------------- ...
- springboot(十三):springboot小技巧
一些springboot小技巧.小知识点 初始化数据 我们在做测试的时候经常需要初始化导入一些数据,如何来处理呢?会有两种选择,一种是使用Jpa,另外一种是Spring JDBC.两种方式各有区别下面 ...
- mango(mango ORM框架介绍)
官网地址:http://www.jfaster.org/ mango的中文名是"芒果",它是一个极速分布式ORM框架.目前已有十多个大型线上项目在使用mango,在某一支付系统中, ...
- 配置java项目的intellij idea的运行环境
才疏学浅,只懂一点点前端的皮毛东西,对于项目运行环境的配置一无所知,今天简单记录一下! 前提:装好了jdk.maven.intellij idea. 1. file菜单->Open...打开从S ...
- 字符截取:cut,格式化输出:printf,字符截取:awk,文件或命令输出编辑:sed
cut 选项 文件名 -f 列号 提取第几列 -d 分隔符 指定分隔符把行分成多列 不能以空格为分隔符. [root@localhost ~]# cat testfile no. name sex s ...
- 转:IT巨头纷纷“卡位” 智能语音成人机交互入口必争之地
http://www.cs.com.cn/xwzx/hwxx/201707/t20170712_5368595.html 随着物联网的迅速发展,作为重要接口的智能语音技术已成为国内外IT巨头的必争之地 ...
- webpack 理解
目录 关于此文 在学习webpack之前,我们先去了解它的作用 它与其他其他前端工具(gulp,grunt)有什么差别呢 安装 webpack.config.js 配置结果 webpack 开始简单配 ...