前端基础(三)-- JavaScript
JavaScript简介
JavaScript可以说是世界上最流行的脚本语言,这也正达到了作者给这种语言取名字的目的,由于当时Java很火,所以这哥们儿为了JavaScript也能火起来就取了个相似的名字,并希望JavaScript也能火起来,所以现在就有了很多程序员的段子,一脸黑线。
JavaScript是一种解释型的动态语言,我们知道用JavaScript开发web,然而自从有了Node.js,前端程序员瞬间变成了全栈,自从facebook开源了跨平台开发框架react-native,前端程序员瞬间能写源生客户端了,最近阿里也开源了更轻量级Weex,而且最近RoyLi的创业公司搞出来一个硬件产品Ruff,也可以用JavaScript开发,前端程序员瞬间成为了真正的全栈,JavaScript这是要大一统的节奏啊,想想也是醉了。所以,是时候学一波JavaScript了。
虽然之前基本没怎么用过JavaScript,但是也学过像python这种动态语言,说实话,学完JavaScript就有一种感觉这是tm什么玩意儿的感觉,可以说非常怪异,也可以说非常magic,用一个词形容感觉特别合适,喜欢NBA的童鞋应该深有感触,妖刀–GinoBili。真的是太妖了。幸好有最新的标准ECMAScript 6(以下简称ES6)写起来还能轻松点,不然真的坑太多了。下文会对比着说。
数据类型
JavaScript中一个有5种基本数据类型:
- 数字,包括整数和浮点和NaN(not a number)
- 字符串
- 布尔值
- undefined:未声明或者声明了却未赋值
- null:空值,与undefined的区别在于这是一个已经声明的变量
JavaScript,并且任何不属于基本数据类型的东西都是对象。
数组,Map什么的就不写了。
变量
说到变量我真是喷出一口老血,太特么容易坑了。
由于是动态语言,所以不需要指定变量的类型,可以在运行时绑定。声明变量用var(variable)。
作用域
为什么说容易坑呢,先看个列子
竟然打印了10。法克。
用var声明的变量的作用域是函数级别的,也就是说在函数内部有效,不同于java等静态语言变量声明是块级别的。由于for语句属于函数,所以变量i的作用域就是整块代码。
那为啥会打印出f**k?如果不用var声明变量,那么默认就是全局变量,也就是说b其实是个全局变量..醉了。JavaScript中有一种严格模式,在JavaScript代码的第一行写上:'use strict';,就会强制通过var声明变量,避免发生错误。
另外一种办法就是,ES6新增了let命令用来声明变量,该变量的作用域是块级别的,把上面的例子var改为let最终输出就是变成6。
变量提升
又为啥这么说坑呢,看例子
结果竟然是undefined,再法克。
上面函数的意思是匿名函数立即执行的写法。JavaScript的函数定义有个特点,它会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部。所以上面代码在JavaScript引擎看来是这样的:
那有什么办法解决呢?
用let,用let,用let。
坑爹的this
一般正常的面向对象的语言this就是指向对象本身,而JavaScript很坑爹,this指向视情况而定,用好了是指向对象本身,用不好就指向全局对象(非strict模式)或者指向undefined(strict模式),全局对象是指web中是window,Node.js中是global。
- 指向对象本身
obj.fuc();- 由于
JavaScript中函数也是对象,调用函数对象的call()、apply(),第一个参数传入要绑定的this对象。
- 指向全局对象或者
undefined- 没有绑定对象
- 间接调用方法
var a = obj.func(), a(); - 方法中返回闭包,闭包中使用了
this
总之,this坑很多,能不用最好不用。
闭包
闭包(closure)是一种包含了外部函数的参数和局部变量的返回函数。换句话说,闭包就是携带状态的函数,并且它的状态可以完全对外 隐藏起来。
|
|
Android开发中常用的回调就是一种闭包,只不过是用对象方法的方式表达,而JavaScript中函数也是一种对象,所以无需多余的对象引用。
类似Java的lambda表达式,ES6中可以用箭头函数定义匿名函数:
|
|
面向对象
JavaScript是一种面向对象的语言,刚才已经说了除基本数据类型外,所有的东西都是对象,但是又跟正常的面向对象语言不一样。像Java、C++这种大多数面向对象语言,类和实例是面向对象的基础,而JavaScript不区分类和实例的概念,而是通过原型(prototype)来实现面向对象编程。
JavaScript对每个创建的对象都会设置一个原型,指向它的原型对象。并且有一个属性查找原则,当我们用obj.xxx访问一个对象的属性时,JavaScript引擎先在当前对象上查找该属性,如果没有找到,就到其原型对象上找,如果还没有找到,就一直上溯到Object.prototype对象,最后,如果还没有找到,就只能返回undefined。
创建对象
JavaScript面向对象基于原型实现,这就导致其用法比较灵活,也足够简单,缺点就是比较难理解,容易出错。下面是几种创建对象的方法。
直接用{ ... }创建一个对象
Student就是一个对象
原型链是这样的:
JavaScript的原型链和Java的Class区别就在,它没有“Class”的概念,所有对象都是实例,所谓继承关系不过是把一个对象的原型指向另一个对象而已。
Object.create()
传入一个原型对象作为参数,并创建一个基于该原型的新对象,但是新对象什么属性都没有
|
|
构造函数
JavaScript的构造函数就是普通函数
|
|
原型链是这样的
也就是说,xiaoming的原型指向函数Student的原型。验证一下
封装构造函数,以对象作为初始化参数
|
|
这个createStudent()函数有几个巨大的优点:一是不需要new来调用,二是参数非常灵活,可以不传,也可以传一个对象。
原型继承
一张图看懂上面的关系
xiaoming的原型指向Student的prototype对象,这个原型对象有个constuctor属性,指向Student()函数本身。
上面可以看到xiaoming.hello() !== xiaohong.hello(),各自的hello()函数实际上是两个不同的函数,如果我们要创建共享的hello函数,根据属性查找原则,只需要把函数定义在他们共同所指的原型对象上来就可以了。
那么假如我们想从Student扩展出PrimaryStudent,使得新的基于PrimaryStudent创建的对象不但能调用PrimaryStudent.prototype定义的方法,也可以调用Student.prototype定义的方法。也就是说原型链是这样的
那要怎么做呢?
我们可以定义一个空函数F,用于桥接原型链,并将其封装起来,隐藏F的定义,代码如下
使用
原型链图如下
类继承
说实话,你让我写原型继承,我的内心其实是拒绝的,这特么都是些什么啊乱七八糟的,继承要写这么多,而且很容易出错有没有!那么有没有类似Java这种类继承的方式呢,答案是当然有,ES6早就为我们准备好了。
ES6中增加了新的关键字class用于定义类。extends用于实现类的继承。
创建对象的方式跟原型继承一样,new就可以了。
继承:
是不是炒鸡简单,跟Java的类继承基本一模一样。这样写跟原型继承的写法在JavaScript引擎看来完全一样。
总结
这篇基本描述了JavaScript的一些注意容易踩坑的点和与Java面向对象实现不同的点。像一些基础的集合、字符串等都没写。当然还有一些前端用的比较多的比如DOM、AJAX、jQuery就不写了,大概浏览下就好了。
另外一点要说的就是,对于我们这些非前端工程师来说,ES6中定义了的一律用ES6的,而且像Node.js这种脱离了浏览器引擎的框架已经完全支持ES6的写法。
学完JavaScript就得学Node.js啊。下一篇带来Node.js基础和实战。
参考
《JavaScript面向对象编程指南(第二版)》
阮一峰的网络日志
廖雪峰 JavaScript教程
JavaScript秘密花园
本文链接: http://w4lle.com/2016/05/31/JavaScript/
版权声明:本文为 w4lle 原创文章,可以随意转载,但必须在明确位置注明出处!
本文链接: http://w4lle.com/2016/05/31/JavaScript/