概述
javascript是一个神奇语言,简单易学,特别是近些年nodejs服务端js的崛起,再加上reactjs,angualrjs等等,可以说现在js是做火的一种语言。程序员很实在,够用就好,我们用大量的js超集,js库,可以说人人都会js,但几乎是很少会花大量时间学习javascript,遇到坑,经常会不知其所以然。一大堆时髦的各种js经常挂嘴边,各种吹水。经常用的的是jquery。基本上就没从头好好看过javascript。所以这就是我的新的学习目标,从头好好理一理下javascript。
虽然开头的比较基础的,大家都会,但我觉得好好静下心来学习最基础的东西,未尝不会没有收获。
准备工作
js目前最新标准是ecmascript6,简称es6,所有浏览器都在像es6标准靠拢,我们测试运行javascript可以用身边随便哪个最新版的浏览器,
如果是chrome,按alt+command+I (windows下是ctrl+shift+I) 弹出开发者工具,选择console选项页,就可以输入js,换行按shift+回车换行
如果安装了node.js的同学也可以直接用 node.js命令行执行js (注意,某些方法,比如alert,prompt 这些是浏览器的方法,node.js是不会执行的)
输入输出
为方便代码编写,我们定义下常用的输入输出,输出主要用console.log(),输入用prompt,类似下图
类型替换
javascript类型可以随意替换,见下图,两个输入颜色不一样
相同判断
对于“123”以及123,是否相同,当字符串和数字比较的时候,js会自动把字符串“123”转为数字123,然后再对比123==123,那当然相同了,
但这样很容易出错,所以如果js要判断完全相同的话用3个等号见下
备注
良好的代码必须要有很漂亮的备注说明,与大多数语言一样,单行备注用//,多行备注用/* */,
我觉得好的备注应该要解析为什么这么做,而不是解析如何去做
var a = 24; // 24 代表着活着的意义
/* 为什么是24呢, 我随便写的一个数字, 没意义啦 */ var a = 24;变量
变量用var定义,在js中变量类型会隐性转换 ,如下图,数字和字符串相加的时候会自动转为字符串,相乘的时候字符串又回自动转为数字,如果需要指定格式,则可以用显性转换,比如下图的Number,显性将字符串专为数字。
var xa=2; var xb='3'; console.log(xa+xb); console.log(xa+Number(xb)); console.log(xa*xb); 23 5 6
常量
在es6规范中新增了常量的定义,使用const定义常量,常量不可更改,常量名建议以全大写名来定义,词组之间用下划线隔开
const TAX_RATE=0.17; var price=100; console.log((price*TAX_RATE).toFixed(2)); 17.00
块
块以大括号对{。。。}隔开,左大括号在js里建议不要分行,同行里显示。类似下图,不要用大括号单独为一行
if (amount > 10) { // <-- block attached to `if` amount = amount * 2; console.log( amount ); // 199.98 }条件
和大多数语言一样,条件就是if (){} else {}这种格式,
注意:在js中条件中的值为bool类型,如果条件的值不是bool类型,会隐性转换为truthy/falsy,在js中,除了null,undefined,0,-0,NaN 和''会转为falsy,其他的都会转为truthy,字符串'0'也会转换为truthy
const ACCESSORY_PRICE = 9.99; var bank_balance = 302.13; var amount = 99.99; amount = amount * 2; // 钱还够不够呀 if ( amount < bank_balance ) { console.log( "买买买" ); amount = amount + ACCESSORY_PRICE; } // 没钱 else { console.log( "看看就好" ); }
还有一种条件就是switch(){case :;}
switch (a) { case 2: case 10: // some cool stuff break; case 42: // other stuff break; default: // fallback }还有就是三元运算符 ?:类似下图
var b = (a > 41) ? "hello" : "world";
循环
循环可以用while(){} ,或者do{} while() ,区别就是第一次要不要做判断。还有一种就是for (var i=0;i<10;i++) for循环,跟其他语言一样
函数
函数以function 标注,类似下行代码, function 带函数名,括号中带参数,后面通过函数名调用
function printAmount(amt) { console.log( amt.toFixed( 2 ) ); } var amount = 99.99; printAmount( amount * 2 );作用域
作用域就是语法的有效范围,js中每个函数都是自己独立的作用域,在独立的作用域中定义的变量只有该域以及子域才能访问,。如下代码所示,
function one() { // 这个a只存在于one函数 var a = 1; console.log( a ); } function two() { // 这个a只存在于two函数 var a = 2; console.log( a ); } one(); // 1 two();
function foo() { var a = 1; if (a >= 1) { let b = 2; while (b < 5) { let c = b * 2; b++; console.log( a + c ); } } } foo();// 5 7 9
类型
js现有的类型有以下几种:,通过typeof 运算符可以得出变量的类型,注意,typeof 返回的是一串字符串。如果是函数的话,typeof 会返回"function"
string // 字符串
number // 字符串
boolean // true false 布尔类型
null and undefined // 空值
object // 对象基类
symbol // es6中新增的标注
var a; typeof a; // "undefined" a = "hello world"; typeof a; // "string" a = 42; typeof a; // "number" a = true; typeof a; // "boolean" a = null; typeof a; // "object" -- 这个明显是bug a = undefined; typeof a; // "undefined" a = { b: "c" }; typeof(a); // "object"Objects数据
js 的object其实就是键值对,用{}包括,以:隔开键值,
var obj = { a: "hello world", b: 42, c: true }; obj.a; // "hello world" obj.b; // 42 obj.c; // true obj["a"]; // "hello world" obj["b"]; // 42 obj["c"]; // true
Array数组
array数组,用[]包括,这些规则其实跟json是一样的。
var arr = [ "hello world", 42, true ]; arr[0]; // "hello world" arr[1]; // 42 arr[2]; // true arr.length; // 3 typeof arr; // "object"Functions函数对象
上段已经简要讲过了function,function本身就是object的子类,所以function可以new,并且可以设置属性
function foo() { return 42; } foo.bar = "hello world"; typeof foo; // "function" typeof foo(); // "number" typeof foo.bar; // "string"js函数有点特别之处,就是定义的函数,函数名本身也视为一个变量,也就是说他可以把函数名作为值赋值给其他变量
var foo = function() { // 匿名函数 }; var x = function bar(){ // bar是函数名,同时作为值赋值给x };
自带方法
js中有自带一些方法,如下,调用这些方法的时候,
比如调用a.toUpperCase, a会封装成string类型的object对象,然后调用string对象的toUpperCase()方法,
调用b.toFixed(4)的时候,b会封装成number类型的object对象,然后调用number对象的toFixed()方法
var a = "hello world"; var b = 3.14159; a.length; // 11 a.toUpperCase(); // "HELLO WORLD" b.toFixed(4); // "3.1416"转换
转换分2种,一种隐性转换,一种显性转换,显性转换就是用函数强制转换,隐性则是在运算的时候自动转换。
var a = "42";
var b = Number( a ); //显性转换
a; // "42"
b; // 42 -- the number!
var a = "42"; var b = a * 1; // 这里 "42" 隐性转换为 42 a; // "42" b; // 42 -- the number!逻辑运算
== (数值相同) ===(数值相同并且类型相同) !=(数值不相同) !==(数值不同或者类型不同)
>(大于符号,不只数字可以判断,字符串也可以做大小判断,以ascii数字排序为依据,比如 'b'>'a' , 'a'>'B'
< (小于符号,与>相反)
注意:
如果类型相同,则直接比较,如果不同,则会做转换
如果一方是bool类型,则bool类型会先隐性转为Number //true 为1,false为0
如果字符和数字做比较,则字符会转换为Number ,如果转换失败,则值为NaN,NaN跟任何类型对比都是false
如果是object,则先取getValue ,如果没有值,则取toString,如果都取不到,就是个独立的object
比如[] 与[] 是不同的, []==[] []>[] []<[] 都是false
null==undefined
undefined 除了与null比较是true,与任何数据做比较,不管>,< == 都是false,类似NaN
null与数据对比时,可以看成是0与数字对比,但0==null 为false
比如42>‘foo’, 就会 先把'foo'转换为number,然后得到invalid number NaN
var a = 41; var b = "42"; var c = "43"; a < b; // true b < c; // true
var a = 42; var b = "foo"; a < b; // false a > b; // false a == b; // false
Strict Mode 严格模式
Strict Mode是es5新加的模式,添加“use strict"来表明js必须遵守新的es5以上的规范,如下,限定use strict 之后,变量名必须提前定义,否则的话就会报错,
function foo() { "use strict"; // turn on strict mode a = 1; // `var` missing, ReferenceError } foo();
javascript编码要尽量使用"use strict",养成良好的js编码规范。
立即执行函数
立即执行函数就是马上执行,而不要调用的函数,如下,(function fName(){...})(); 用括号把函数括起来,并带()执行,
var a = 42; (function IIFE(){ var a = 10; console.log( a ); // 10 })(); console.log( a ); // 42这个主要作用在于保护变量,因为javascript中没用私有作用域的概念,如果在多人开发的项目上,你在全局或局部作用域中声明了一些变量,可能会被其他人不小心用同名的变量给覆盖掉,根据javascript函数作用域链的特性,可以使用这种技术可以模仿一个私有作用域,用匿名函数作为一个“容器”,“容器”内部可以访问外部的变量,而外部环境不能访问“容器”内部的变量,所以( function(){…} )()内部定义的变量不会和外部的变量发生冲突,俗称“匿名包裹器”或“命名空间”
Closure闭包
闭包是js中非常非常重要的一个技能,但很少人花时间去关注,大概讲解一下,闭包简单讲就是记住函数(包括函数作用域中的变量),即使函数已经结束,回调该函数的时候,记住函数(同样包括该作用域中的变量)
function makeAdder(x) { // 参数 `x` 作为内部变量 // 内部函数 `add()` 使用 `x`, 所以 // 就存在一个闭包 function add(y) { return y + x; }; return add; }我们来调用一个makeAdder
// plus one `plusTen` 定义为对`add(..)`函数的引用,有一个来自于makeAdderx参数的闭包
var plusOne = makeAdder( 1 );
var plusTen = makeAdder( 10 ); plusOne( 3 ); // 4 <-- 1 + 3 plusOne( 41 ); // 42 <-- 1 + 41 plusTen( 13 ); // 23 <-- 10 + 13
moudle模块模式
closure闭包最常见于moudle模块,函数本身是有私有域,要访问函数内部方法,变量就可以用模块模式 举例见下
function User(){ var username, password; function doLogin(user,pw) { username = user; password = pw; // do the rest of the login work } var publicAPI = { login: doLogin }; return publicAPI; } // create a `User` module instance var fred = User(); fred.login( "fred", "12Battery34!" );
var fred=User(); 这里没必要加new ,因为User本身是个函数,并不像个class需要实例化,不用new可以节省系统开销。
每个User() 都是独立的,多个user()之间变量互相不相干
dologin 函数有包涵username,password的闭包,所以user()执行结束后,还可以通过publicApi来访问username,password
这里调用login就实现了调用dologin相同的效果。
this标识符
this是经常用的标识,但却很容易让人搞糊涂。 this在函数中调用,那么指的就是调用方,举例见下
function foo() { console.log( this.bar ); } var bar = "global"; var obj1 = { bar: "obj1", foo: foo }; var obj2 = { bar: "obj2" }; // -------- foo(); // "global" obj1.foo(); // "obj1" foo.call( obj2 ); // "obj2" new foo(); // undefined
注意:如果是strict mode,上面foo()会报错,其他3个调用的确正常,因为严格模式下不允许直接这么访问全局变量bar
Prototype原型
原型就是我们调用一个object的属性,如果属性不存在,那么就会查找内部的原型引用找到该属性值
var foo = { a: 42 }; // create `bar` and link it to `foo` var bar = Object.create( foo ); bar.b = "hello world"; bar.b; // "hello world" bar.a; // 42 <-- delegated to `foo`
新旧标准javascript兼容
ecma script 去前是es6版,但后续都是以年来定义,今年es2016,后面es2017,es2018,每年都有新的语法出现,但主流的浏览器都没法完全支持这些新语法,怎么办呢
难道我们用新js一定要等很多年,等到支持了,再用新的js标准吗,不用担心,有2种技术可以让你的新js用在旧浏览器上
Polyfilling
Polyfill就是让新版js 函数可以运行在旧的浏览器上,举个例子,es6中作废 isNaN 用Number.isNaN,那么我们用Number.isNaN的时候,怎么polyfill让旧版本支持新特性呢,这个就简单了,直接增加以下方法就可以了
if (!Number.isNaN) { Number.isNaN = function isNaN(x) { return x !== x; }; }当然,其实我们没必要造轮子,现在已经有现成写好的shim js,直接下载下来即可,调用地址: https://github.com/es-shims/es5-shim, https://github.com/es-shims/es6-shim
Transpilling
Polyfill可以解决新版函数在旧浏览器运行的问题,但无法解决新的语法在旧浏览器会报错的问题,那么还有一个好的办法,就是把新版的js转换成旧版的js,这个过程就叫Transpilling,就是说你写的是新标准的js,其实运行的是旧标准的js,那么为什么不直接用旧标准的写呢,还要转换。用新版写的好处在于
1:用新的js写代码更易读,更容易维护
2:浏览器发展很快,js标准现在也是发展越来越快,现在新版不久就会变成标准。作为程序员,你需要掌握这些新家伙。
举个例子,函数参数默认值是es6中才有的,旧版不支持
function foo(a = 2) { console.log( a ); } foo(); // 2 foo( 42 ); // 42
function foo() { var a = arguments[0] !== (void 0) ? arguments[0] : 2; console.log( a ); }
一些转换工具:Babel (https://babeljs.io),Traceur (https://github.com/google/traceur-compiler)
Non-javascript
现实中,js就是用在浏览器上,如果说其实我们常写的语句,就不是javascript,你可能会奇怪,最常见的就是Dom api,例如
var el = document.getElementById( "foo" );当你在浏览器中运行的时候, document 是个全局变量,实际上它并不是js引擎提供的变量。看上去是js object,实际上是浏览器一个特殊的object,称之为host object
getElementById看上去是函数,其实是一个接口,用于控制浏览器的dom元素
类似的还有alert ,console.log() ,这些都并不是javascript自带的
尾声
大致记录了下javascript,其实现在es标准发展太快了,很多知识都在更新,对我来说,记录一遍,还是很有收获。希望读者你也能有一点点收获。