1 | <swiper class="swiper-slide"> |
ES6 Promise
1.Promise含义
特点:
- 异步编程的一种解决方案(相较于回调函数和事件更合理,更强大)
- 一个保存着某个未来才会结束的事件(通常是一个异步操作)的结果的容器
- 对象的状态不受外界影响(pending进行中,fulfilled已成功,rejected已失败)
- 一旦状态改变,就不会再次变化,任何时候都可以得到这个结果(状态变化,只有两种可能:从pending转为fulfilled;从pending转为rejected)
- 将异步操作以同步操作的流程表达出来,避免层层嵌套的回调函数
缺点:
- 无法取消Promise,一旦新建会立即执行,无法中途取消
- 如果不设置回调函数,Promise内部抛出的错误不会反应到外部
- 当处于pending状态时,无法得知目前进行到那个阶段
2.基本用法
1 | const promise = new Promise(function(resolve,reject){ |
Promise构造函数接受一个函数作为参数,该参数的两个参数分别是resolve和reject。他们是两个函数,由JS引擎提供,不需要自己部署。
resolve函数的作用是将Promise对象的状态从未完成(pending)转换为已成功(resolved),在异步操作成功时调用,并将异步操作的结果作为参数传递过去。
reject函数的作用是将Promise对象的状态从未完成(pending)转换为已失败(rejected),在异步操作失败时调用,并将异步操作报出的错误作为参数传递出去。
Promise对象生成以后,可以使用then方法分别指定resolved状态和rejected状态的回调函数。
1 | promise.then(function(value){ |
then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数时Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。
1 | function timeout(ms){ |
timeout方法返回一个Promise实例,表示一段时间以后才会发生的结果。过了指定的时间(ms参数)以后,Promise实例的状态变为resolved,就会触发then方法绑定的回调函数。
1 | let promise = new Promise(function(resolve,reject){ |
Promise新建后会立即执行,所以首先输出的是Promise。然后then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出。
1 | //异步加载图片示例 |
使用Promise包装了一个图片加载的异步操作,如果加载成功,就调用resolve方法,否则就调用reject方法。
1 | //Promise对象实现Ajax操作 |
getJSON是对XMLHttpRequest对象的封装,用于发出一个针对JSON数据的HTTP请求,并且返回一个Promise对象。
在getJSON内部,resolve函数和reject函数调用时,都带有参数。
如果调用resolve函数和reject函数时带有参数,那么他们的参数会被传递给回调函数。reject函数的参数通常是ERROR对象的实例,表示抛出的错误,resolve函数的参数除了正常的值以外,还可能是另一个Promise实例。
1 | //案例 |
p1和p2都是Promise的实例,但是p2的resolve方法将p1作为一个参数,即一个异步操作的结果是返回另一个异步操作。
需要注意的是:这时p1的状态就会传递给p2,也就是说p1的状态决定了p2的状态。如果p1的状态时pending,那么p2的回调函数就会等待p1的状态改变,如果p1的状态已经是resolved或者rejected,那么p2的回调函数将会立刻执行。
1 | const p1 = new Promise(function(resolve,reject){ |
p1是一个Promise,3秒之后变为rejected。p2的状态在1秒之后改变,resolve方法返回的是p1.由于p2返回的是另一个Prmise,导致p2自己的状态无效了,由p1的状态决定p2的状态。所以,后面的then语句都变成针对后者(p1).又过了2秒,p1变为rejected,导致触发catch方法指定的回到函数。
调用resolve或reject并不会终结Promise的参数函数的执行。
1 | new Promise((resolve,reject)=>{ |
调用resolve(1)后,后面的console.log(2)还是会执行,并且会首先打印出来。这是因为立即resolved的Promsie是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。
一般来说,调用resolve或reject以后,Promise的使命就完成了,后续操作应该放到then方法里面,而不应该直接写在resolve或reject的后面。所以,最好在他们前面加上return语句,这样就不会有意外。
1 | new Promise((resolve,reject)=>{ |
3.Promise方法
Promise.prototype.then()
Promise实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为Promise实例添加状态改变时的回调函数。前面说过,then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。
then方法返回的是一个新的Promise实例(非原来的Promise实例)。因此可以采用链式写法:then方法后面再调用另一个then方法。
1 | getJSON("/posts.json").then(function(json){ |
使用then方法,依次指定了两个回调函数。第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。
采用链式的then方法,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。
1 | getJSON("/post/1.json").then(function(post){ |
第一个then方法指定的回调函数,返回的是另一个Promise对象。这是,第二个then方法指定的回调函数就会等待这个新的Promise对象状态发生变化。如果变为resolved,就调用第一个回调函数,如果状态变为rejected,就调用第二个回调函数。
采用箭头函数,可以使代码更简洁。
1 | getJSON("/post/1.json").then( |
Promise.prototype.catch()
Promise.prototype.catch方法是.then(null,rejection)或.then(undefined,rejected)的别名,用于指定发生错误时的回调函数。
1 | getJSON("/posts.json").then(function(posts){ |
getJSON方法返回一个Promise对象,如果该对象状态变为resolved,则会调用then方法指定的回调函数;如果异步操作抛出错误,状态就会变为rejected,就会调用catch方法指定的回调函数,处理这个错误。另外,then方法指定的回调函数,如果运行中抛出错误,也会被catch方法捕获。
1 | p.then((val) => console.log('fulfilled:', val)) |
1 | const promise = new Promise(function(resolve, reject) { |
promise抛出一个错误,就会被catch方法指定的回调函数捕获。
1 | // 写法一 |
reject方法的作用,等同于抛出错误。
如果Promise状态已经变成resolved,再抛出错误是无效的。
1 | const promise = new Promise(function(resolve,reject){ |
Promise在resolve语句后面,再抛出错误,不会被捕获,等于没有抛出。因为Promise的状态一旦改变,就永久保持该状态,不会再变了。
Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。
1 | getJSON('/post/1.json').then(function(post) { |
一共有三个Promise对象:一个由getJSON产生,两个由then产生。他们之中任何一个抛出的错误,都会被最后一个catch捕获。
一般来说,不建议在then方法里面定义Reject状态的回调函数(即then的第二个参数),总是使用catch方法。
1 | // bad |
第二种写法要好于第一种写法,理由是第二种写法可以捕获前面then方法执行中的错误,也更接近同步的写法(try/catch)。因此,建议总是使用catch方法,而不使用then方法的第二个参数。
跟传统的try/catch代码块不同的是,如果没有使用catch方法指定错误处理的回调函数,Promise对象抛出的错误不会传递到外层代码,即不会有任何反应。
1 | const someAsyncThing = function() { |
someAsyncThing函数产生的Promise对象,内部有语法错误。浏览器运行到这一行,会打印出错误提示:ReferenceError:x is not defined,但不会退出进程、终止脚本执行,2秒后还是会输出123.这就是说,Promise内部的错误不会影响到Promise外部的代码,通俗的说法就是“Promise会吃掉错误”。
这个脚本放在服务器执行,退出码就是0(即表示执行成功)。不过Node有一个unhandledRejection事件,专门监听未捕获的reject错误,上面的脚本会触发这个事件的监听函数,可以在监听函数里面抛出错误。
1 | promise.on("unhandledRejection",function(err,p){ |
unhandledRejection事件的监听函数有两个参数,第一个是错误对象,第二个是报错的Promise实例,它可以用来了解发生错误的环境信息。
Node有计划在未来废除unhandledRejection事件。如果Promise内部有未捕获的错误,会直接终止进程,并且进程的退出码不为0。
1 | const promise = new Promise(function (resolve, reject) { |
Promise指定在下一轮“事件循环”再抛出错误。到了这个时候,Promise的运行已经结束了,所以这个错误是在Promise函数体外抛出的,会冒泡到最外层,成了未捕获的错误。
一般总是建议Promise对象后面要跟catch方法,这样可以处理Promise内部发生的错误。catch方法返回的还是一个Promise对象,因此后面还可以接着调用then方法。
1 | const someAsyncThing = function() { |
代码运行结束catch方法指定的回调函数,会接着运行后面那个then方法指定的回调函数。如果没有报错,则会跳过catch方法。
1 | Promise.resolve() |
上面的代码中因为没有报错,跳过了catch方法,直接执行后面的then方法。此时要是then方法里面报错,就与前面的catch无关了。
catch方法之中还能再抛出错误。
1 | const someAsyncThing = function() { |
上面的代码中,catch方法抛出一个错误,因为后面没有别的catch方法了,导致这个错误不会被捕获,也不会传递到外层。
1 | someAsyncThing().then(function() { |
上面的代码中,第二个catch方法用来捕获前一个catch方法抛出的错误。
Promise.prototype.finally()
finally方法用于指定不管Promise对象最后状态如何,都会执行的操作。该方法是ES2018引入标准的。
1 | promise.then(result=>{...}) |
不管Promise最后的状态,在执行完then或catch指定的回调函数以后,都会执行finally方法指定的回调函数。
服务器使用Promise处理请求,然后使用finally方法关掉服务器。
1 | server.listen(post) |
finally方法的回调函数不接受任何参数,这意味着没有办法知道,前面的Promise状态到底是fulfilled还是rejected。这表明finally方法里面的操作,应该是与状态无关的,不依赖于Promise的执行结果。
finally本质上是then方法的特例。
1 | promise.finally(()=>{ |
如果不使用finally方法,同样的语句需要为成功和失败两种情况各写一次。有了finally方法,则只需要写一次。
1 | Promise.prototype.finally = function(callback){ |
上面的代码中,不管前面的Promise是fulfilled还是rejected,都会执行回调函数callback。finally方法总是会返回原来的值。
1 | // resolve 的值是 undefined |
Promise.all()
Promise.all()方法用于将多个Promise实例,包装成一个新的Promise实例。
1 | const p = Promise.all([p1,p2,p3]); |
Promise.all()方法接受一个数组作为参数,p1、p2、p3都是Promise实例,如果不是则会先调用Promise.resolve方法,将参数转为Promise实例,再进行处理。另外,Promise.all()方法的参数可以不是数组,但必须具有Iterator接口,且返回的每个成员都是Promise实例。
p的状态由p1、p2、p3决定,分成两种情况:
- 只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
- 只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
1 | const promises = [2,3,5,7,11,13].map(function(id){ |
promises是包含6个Promise实例的数组,只有这6个实例的状态都变成fulfilled,或者其中有一个变为rejected,才会调用Promise.all
方法后面的回调函数。
1 | const databasePromise = connectDatabase(); |
booksPromise
和userPromise
是两个异步操作,只有等到它们的结果都返回了,才会触发pickTopRecommendations
这个回调函数。
注意,如果作为参数的 Promise 实例,自己定义了catch
方法,那么它一旦被rejected
,并不会触发Promise.all()
的catch
方法。
1 | const p1 = new Promise((resolve, reject) => { |
p1
会resolved
,p2
首先会rejected
,但是p2
有自己的catch
方法,该方法返回的是一个新的 Promise 实例,p2
指向的实际上是这个实例。该实例执行完catch
方法后,也会变成resolved
,导致Promise.all()
方法参数里面的两个实例都会resolved
,因此会调用then
方法指定的回调函数,而不会调用catch
方法指定的回调函数。
如果p2
没有自己的catch
方法,就会调用Promise.all()
的catch
方法。
1 | const p1 = new Promise((resolve, reject) => { |
Promise.race()
Promise.race()
方法同样是将多个Promise实例包装成一个新的Promise实例。
1 | const p = Promise.race([p1,p2,p3]); |
只要p1,p2,p3之中有一个实例率先改变状态,p的状态也会跟着改变。那个率先改变的Promise实例的返回值,就传递给p的回调函数。
Promise.race()
方法的参数与Promise.all()
方法一样,如果不是 Promise 实例,就会先调用下面讲到的Promise.resolve()
方法,将参数转为 Promise 实例,再进一步处理。
如果指定时间内没有获得结果,就将 Promise 的状态变为reject
,否则变为resolve
。
1 | const p = Promise.race([ |
如果 5 秒之内fetch
方法无法返回结果,变量p
的状态就会变为rejected
,从而触发catch
方法指定的回调函数。
Promise.allSettled()
Promise.allSettled()
方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled
还是rejected
,包装实例才会结束。
1 | const promises = [ |
上面代码对服务器发出三个请求,等到三个请求都结束,不管请求成功还是失败,加载的滚动图标就会消失。
该方法返回的新的 Promise 实例,一旦结束,状态总是fulfilled
,不会变成rejected
。状态变成fulfilled
后,Promise 的监听函数接收到的参数是一个数组,每个成员对应一个传入Promise.allSettled()
的 Promise 实例。
1 | const resolved = Promise.resolve(42); |
上面代码中,Promise.allSettled()
的返回值allSettledPromise
,状态只可能变成fulfilled
。它的监听函数接收到的参数是数组results
。该数组的每个成员都是一个对象,对应传入Promise.allSettled()
的两个 Promise 实例。每个对象都有status
属性,该属性的值只可能是字符串fulfilled
或字符串rejected
。fulfilled
时,对象有value
属性,rejected
时有reason
属性,对应两种状态的返回值。
下面是返回值用法的例子。
1 | const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ]; |
有时候,我们不关心异步操作的结果,只关心这些操作有没有结束。这时,Promise.allSettled()
方法就很有用。如果没有这个方法,想要确保所有操作都结束,就很麻烦。Promise.all()
方法无法做到这一点。
1 | const urls = [ /* ... */ ]; |
上面代码中,Promise.all()
无法确定所有请求都结束。想要达到这个目的,写起来很麻烦,有了Promise.allSettled()
,这就很容易了。
Promise.any()
Promise.any()
方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只要参数实例有一个变成fulfilled
状态,包装实例就会变成fulfilled
状态;如果所有参数实例都变成rejected
状态,包装实例就会变成rejected
状态。该方法目前是一个第三阶段的提案 。
Promise.any()
跟Promise.race()
方法很像,只有一点不同,就是不会因为某个 Promise 变成rejected
状态而结束。
1 | const promises = [ |
上面代码中,Promise.any()
方法的参数数组包含三个 Promise 操作。其中只要有一个变成fulfilled
,Promise.any()
返回的 Promise 对象就变成fulfilled
。如果所有三个操作都变成rejected
,那么就会await
命令就会抛出错误。
Promise.any()
抛出的错误,不是一个一般的错误,而是一个 AggregateError 实例。它相当于一个数组,每个成员对应一个被rejected
的操作所抛出的错误。下面是 AggregateError 的实现示例。
1 | new AggregateError() extends Array -> AggregateError |
捕捉错误时,如果不用try...catch
结构和 await 命令,可以像下面这样写。
1 | Promise.any(promises).then( |
下面是一个例子。
1 | var resolved = Promise.resolve(42); |
Promise.resolve()
有时需要将现有对象转为 Promise 对象,Promise.resolve()
方法就起到这个作用。
1 | const jsPromise = Promise.resolve($.ajax('/whatever.json')); |
上面代码将 jQuery 生成的deferred
对象,转为一个新的 Promise 对象。
Promise.resolve()
等价于下面的写法。
1 | Promise.resolve('foo') |
Promise.resolve
方法的参数分成四种情况。
(1)参数是一个 Promise 实例
如果参数是 Promise 实例,那么Promise.resolve
将不做任何修改、原封不动地返回这个实例。
(2)参数是一个thenable
对象
thenable
对象指的是具有then
方法的对象,比如下面这个对象。
1 | let thenable = { |
Promise.resolve
方法会将这个对象转为 Promise 对象,然后就立即执行thenable
对象的then
方法。
1 | let thenable = { |
上面代码中,thenable
对象的then
方法执行后,对象p1
的状态就变为resolved
,从而立即执行最后那个then
方法指定的回调函数,输出 42。
(3)参数不是具有then
方法的对象,或根本就不是对象
如果参数是一个原始值,或者是一个不具有then
方法的对象,则Promise.resolve
方法返回一个新的 Promise 对象,状态为resolved
。
1 | const p = Promise.resolve('Hello'); |
上面代码生成一个新的 Promise 对象的实例p
。由于字符串Hello
不属于异步操作(判断方法是字符串对象不具有 then 方法),返回 Promise 实例的状态从一生成就是resolved
,所以回调函数会立即执行。Promise.resolve
方法的参数,会同时传给回调函数。
(4)不带有任何参数
Promise.resolve()
方法允许调用时不带参数,直接返回一个resolved
状态的 Promise 对象。
所以,如果希望得到一个 Promise 对象,比较方便的方法就是直接调用Promise.resolve()
方法。
1 | const p = Promise.resolve(); |
上面代码的变量p
就是一个 Promise 对象。
需要注意的是,立即resolve()
的 Promise 对象,是在本轮“事件循环”(event loop)的结束时执行,而不是在下一轮“事件循环”的开始时。
1 | setTimeout(function () { |
上面代码中,setTimeout(fn, 0)
在下一轮“事件循环”开始时执行,Promise.resolve()
在本轮“事件循环”结束时执行,console.log('one')
则是立即执行,因此最先输出。
Promise.reject()
Promise.reject(reason)
方法也会返回一个新的 Promise 实例,该实例的状态为rejected
。
1 | const p = Promise.reject('出错了'); |
上面代码生成一个 Promise 对象的实例p
,状态为rejected
,回调函数会立即执行。
注意,Promise.reject()
方法的参数,会原封不动地作为reject
的理由,变成后续方法的参数。这一点与Promise.resolve
方法不一致。
1 | const thenable = { |
上面代码中,Promise.reject
方法的参数是一个thenable
对象,执行以后,后面catch
方法的参数不是reject
抛出的“出错了”这个字符串,而是thenable
对象。
Promise.try()
实际开发中,经常遇到一种情况:不知道或者不想区分,函数f
是同步函数还是异步操作,但是想用 Promise 来处理它。因为这样就可以不管f
是否包含异步操作,都用then
方法指定下一步流程,用catch
方法处理f
抛出的错误。一般就会采用下面的写法。
1 | Promise.resolve().then(f) |
上面的写法有一个缺点,就是如果f
是同步函数,那么它会在本轮事件循环的末尾执行。
1 | const f = () => console.log('now'); |
上面代码中,函数f
是同步的,但是用 Promise 包装了以后,就变成异步执行了。
那么有没有一种方法,让同步函数同步执行,异步函数异步执行,并且让它们具有统一的 API 呢?回答是可以的,并且还有两种写法。第一种写法是用async
函数来写。
1 | const f = () => console.log('now'); |
上面代码中,第二行是一个立即执行的匿名函数,会立即执行里面的async
函数,因此如果f
是同步的,就会得到同步的结果;如果f
是异步的,就可以用then
指定下一步,就像下面的写法。
1 | (async () => f())() |
需要注意的是,async () => f()
会吃掉f()
抛出的错误。所以,如果想捕获错误,要使用promise.catch
方法。
1 | (async () => f())() |
第二种写法是使用new Promise()
。
1 | const f = () => console.log('now'); |
上面代码也是使用立即执行的匿名函数,执行new Promise()
。这种情况下,同步函数也是同步执行的。
鉴于这是一个很常见的需求,所以现在有一个提案,提供Promise.try
方法替代上面的写法。
1 | const f = () => console.log('now'); |
事实上,Promise.try
存在已久,Promise 库Bluebird
、Q
和when
,早就提供了这个方法。
由于Promise.try
为所有操作提供了统一的处理机制,所以如果想用then
方法管理流程,最好都用Promise.try
包装一下。这样有许多好处,其中一点就是可以更好地管理异常。
1 | function getUsername(userId) { |
上面代码中,database.users.get()
返回一个 Promise 对象,如果抛出异步错误,可以用catch
方法捕获,就像下面这样写。
1 | database.users.get({id: userId}) |
但是database.users.get()
可能还会抛出同步错误(比如数据库连接错误,具体要看实现方法),这时你就不得不用try...catch
去捕获。
1 | try { |
上面这样的写法就很笨拙了,这时就可以统一用promise.catch()
捕获所有同步和异步的错误。
1 | Promise.try(() => database.users.get({id: userId})) |
事实上,Promise.try
就是模拟try
代码块,就像promise.catch
模拟的是catch
代码块。
JS循环遍历方法及区别
1 | [1] const arr = [7, 8, 9]; |
for in
for in在ES5中便出现了。一般用来遍历对象属性。但也可用于数组遍历,返回的值是数组的索引,因为,严格来说,数组也是一个对象,所对应的属性是每个值的索引。 **for-in只遍历对象自身的和继承的可枚举的属性(摘自 阮一峰ECMAScript 6 入门-对象扩展一章)**。
如[4]、[5]、[6]所示,为数组添加可遍历属性 (所对应描述器descriptor的enumerable属性为true) ,在[7]中用for-in遍历都可遍历出来。
for of
for of为ES6新增遍历方法。其可遍历所有具有 iterator 接口的数据结构。for…of循环内部调用的是数据结构的Symbol.iterator方法(generator函数)。(摘自 阮一峰ECMAScript 6 入门–Iterator 和 for…of 循环一章)。Symbol.iterator方法返回的是一个遍历器,当用for-of去遍历的时候, 自动调用里面的next方法。
如[8], [9]所示,数组原生具备iterator接口(即默认部署了Symbol.iterator属性),for…of循环本质上就是调用这个接口产生的 遍历器。再看?例子 (摘自 阮一峰ECMAScript 6 入门–Iterator 和 for…of 循环一章),对象obj默认调用的遍历器生成器是obj.values即obj[Symbol.iterator] 。
1 | const colorArr = ['red', 'green', 'blue']; |
总结
所以说,(for in)遍历键名和 (for of)遍历键值,这种说法并不严谨。for in确实是遍历属性,而且更是遍历对象自身的和继承的可枚举的属性。但是,for of遍历键值这种说法便不严谨了,比如说我们一开始在[1]中声明的对象obj,键名为a,b, c; 键值为4,5,6;
按照(for of)遍历键值这种说法,用for of遍历obj应该返回键值4,5,6。可是如[10]所示,程序执行报错了。因为对象默认是没有iterator接口的,我们不可以直接遍历,当我们需要遍历对象的属性时,可以调用Object.keys()返回一个数组,用来遍历所有的键名。当我们需要遍历对象的属性值时,应该调用obj.values()返回一个数组,用来遍历所有的键值。并不是说for-of遍历键值,for-of具体遍历并返回的是什么,是视of后面返回的数组对应是什么。就obj这个对象来说,我们可以遍历obj的键值(Object.values(obj)),也可以遍历obj的键名(Object.keys(obj)),更可以遍历对象的obj的键值对(Object.entires(obj))。
同样。for-of遍历数组,数组是默认具有iterator接口的,即arr.values(),具体遍历并返回的是什么,是视of后面返回的遍历器对象是什么。arr的键值(arr.values()),也可以遍历数组arr的键名(arr.keys()),更可以遍历数组的arr的键值对(arr.entires())。
遍历器对象是一个对象,不是数组。如[11]所示,arr.keys() 是对象Object的实例,并不是数组实例。
v-charts vue图表生成插件
在使用 echarts 生成图表时,经常需要做繁琐的数据类型转化、修改复杂的配置项,v-charts 的出现正是为了解决这个痛点。基于 Vue2.0 和 echarts 封装的 v-charts 图表组件,只需要统一提供一种对前后端都友好的数据格式设置简单的配置项,便可轻松生成常见的图表。
官方文档地址: https://v-charts.js.org/#/
Git常用命令
fetch vs pull
git fetch是将远程主机的最新内容拉到本地,用户在检查了以后决定是否合并到工作本机分支中。
而git pull 则是将远程主机的最新内容拉下来后直接合并,即:git pull = git fetch + git merge,这样可能会产生冲突,需要手动解决。
下面是我整理的常用 Git 命令清单。几个专用名词的译名如下。
1 | Workspace:工作区 |
一、新建代码库
1 | # 在当前目录新建一个Git代码库 |
二、配置
Git的设置文件为.gitconfig,它可以在用户主目录下(全局配置),也可以在项目目录下(项目配置)。
1 | # 显示当前的Git配置 |
三、增加/删除文件
1 | # 添加指定文件到暂存区 |
四、代码提交
1 | # 提交暂存区到仓库区 |
五、分支
1 | # 列出所有本地分支 |
六、标签
1 | # 列出所有tag |
七、查看信息
1 | # 显示有变更的文件 |
八、远程同步
1 | $ git remote update --更新远程仓储 |
九、撤销
1 | # 恢复暂存区的指定文件到工作区 |
十、其他
1 | # 生成一个可供发布的压缩包 |
上传本地项目到远程仓库
1、(先进入项目文件夹)通过命令 git init 把这个目录变成git可以管理的仓库
1 | git init |
2、把文件添加到版本库中,使用命令 git add .添加到暂存区里面去,不要忘记后面的小数点“.”,意为添加文件夹下的所有文件
1 | git add . |
3、用命令 git commit告诉Git,把文件提交到仓库。引号内为提交说明
1 | git commit -m 'first commit' |
4、关联到远程库
1 | git remote add origin 你的远程库地址 |
如:
1 | git remote add origin https://github.com/githubusername/demo.git |
5、获取远程库与本地同步合并(如果远程库不为空必须做这一步,否则后面的提交会失败)
1 | git pull --rebase origin master |
6、把本地库的内容推送到远程,使用 git push命令,实际上是把当前分支master推送到远程。执行此命令后会要求输入用户名、密码,验证通过后即开始上传。
1 | git push -u origin master |
*、状态查询命令
1 | git status |
H5前端面试题
HTML5/CSS3
左中右3个DIV,如何让中间的DIV先显示出来?
浏览器的兼容问题你如何解决。
- Webkit前缀
- hack
Position属性的值有哪些?怎样使用?
- relative 相对定位,相对定位不脱离文档流,参考其在原来文档流中的位置,通过 top,bottom,left,right 定位,并 且可以通过z-index进行层次分级。
- absolute 绝对定位,绝对定位脱离文档流,依据最近的已经定位(绝对、相对或固定定位)的父元素,通过 top,bottom,left,right 定位。当父级 position 为 static 时,absolute元素将依据body根元素(浏览器窗口)进行定 位,可以通过z-index进行层次分级。
- fixed 固定定位,固定定位与父元素无关(无论父元素是否定位),直接根据浏览器窗口定位,且不随滚动条拖动 页面而滚动,可通过z-index进行层次分级。
Doctype有什么作用?严格模式与混杂模式有什么区别?
- 声明叫做文件类型定义(DTD),声明的作用为了告诉浏览器该文件的类型。让浏览器解析器知道应该用哪个规范来解析文档。<!DOCTYPE>声明必须在 HTML 文档的第一行,这并不是一个 HTML 标签。
- 严格模式:又称标准模式,是指浏览器按照 W3C 标准解析代码
混杂模式:又称怪异模式或兼容模式,是指浏览器用自己的方式解析代码。
常用浏览器的内核有哪些?
- 360浏览器:Chrome内核和IE内核
- 百度浏览器:IE和Webkit双内核。、
- QQ浏览器:Chromium内核+IE双内核
- 猎豹浏览器:Trident和WebKit。
- 搜狗浏览器:chromium内核
什么是CSS Hack,作用是什么?
- CSS hack是通过在CSS样式中加入一些特殊的符号,让不同的浏览器识别不同的符号(什么样的浏览器识别什么样的符号是有标准的,CSS hack就是让你记住这个标准),以达到应用不同的CSS样式的目的。
- 属性级Hack:比如IE6能识别下划线“_”和星号“*”,IE7能识别星号“*”,但不能识别下划线”_ ”,而firefox两个都不能认识。
对W3C标准有哪些认识和理解
XHTML和HTML有何不同?
在书写方面:
1.XHTML 元素必须被正确地嵌套。****2.XHTML 元素必须被关闭。标签名必须用小写字母。****3.XHTML 文档必须拥有根元素。**
4.XHTML标签必须成双成对
5.html对标签顺序要求不严格,XHTML标签顺序必须正确总而言之xhtml比html更加规范**
知道哪些浏览器的兼容性问题?如何解决?
不同浏览器的标签默认的外补丁和内补丁不同
*{margin:0;padding:0}
什么是语义化的HTML?
- 用最恰当的标签来标记内容。
HTML5为什么只需要写就可以了?
- 声明位于位于HTML文档中的第一行,处于 标签之前。作用:告知浏览器的解析器用什么文档标准解析这个文档。DOCTYPE不存在或格式不正确会导致文档以怪异模式呈现。
HTML5有哪些新特性?移除了哪些元素?如何处理新标签的浏览器兼容性问题?如何区分HTML和HTML5?
- 用于媒介回放的 video 和 audio 元素;
- 本地离线存储 localStorage 长期存储数据,浏览器关闭后数据不丢失;
- sessionStorage 的数据在浏览器关闭后自动删除;
- 语意化更好的内容元素,比如 article、footer、header、nav、section;
- 表单控件,calendar、date、time、email、url、search;
- 新的技术webworker, websockt, Geolocation;
页面编码和被请求的资源编码如果不一致如何处理?
- 对于ajax请求传递的参数,如果是get请求方式,参数如果传递中文,在有些浏览器会乱码,不同的浏览器对参数编码的处理方式不同,所以对于get请求的参数需要使用 encodeURIComponent函数对参数进行编码处理,后台开发语言都有相应的解码api。对于post请求不需要进行编码
不用媒体查询和盒子布局,如何实现自适应?
- 弹性布局:flex; grid 布局;
常见表单元素有哪些?HTML5提供了哪些新的表单元素?
- Text, radio, submit, textarea, file,
SVG图片和普通图片有何不同?有什么优势?
如何实现垂直居中?
什么是FOUC?如何解决?
- 如果使用import方法对CSS进行导入,会导致某些页面在Windows 下的Internet Explorer出现一些奇怪的现象:以无样式显示页面内容的瞬间闪烁,这种现象称之为文档样式短暂失效(Flash of Unstyled Content),简称为FOUC.原因大致为: 1,使用import方法导入样式表。 2,将样式表放在页面底部 3,有几个样式表,放在html结构的不同位置。其实原理很清楚:当样式表晚于 结构性html 加载,当加载到此样式表时,页面将停止之前的渲染。此样式表被下载和解析后,将重新渲染页面,也就出现了短暂 的 花屏现象。解决方法:使用LINK标签将样式表放在文档HEAD中更多
什么是DIV高度塌陷?,如何解决?
父元素在文档流中高度默认是被子元素撑开的,当子元素脱离文档流以后,将无法撑起父元素的高度,也就会导致父元素的高度塌陷。
使用after伪类
设置overflow为一个非默认值,一般都是使用overflow:hidden来开启BFC
开启BFC以后元素将会具有如下特性:
- 2.1 父元素的垂直外边距不会与子元素重叠
- 2.2. 开启BFC的元素不会被浮动元素所覆盖
- 2.3 开启BFC的元素可以包含浮动子元素
两个a标签之间为何有空白?如何解决?
- a标签默认是行内元素,而行内元素之间的回车键、多个空格、多个制表符(tab)等均会按一个空格来处理,这样的话,如果相邻的两个a标签不在同一行(换句话说它们之间有回车换行符),则显示在页面的时候它们之间就会出现一个空格的间隙。
如何实现响应式布局?
- 媒体查询 盒子布局 flex弹性布局 grid 布局
如何让div居中?如何居中一个浮动元素?如何让绝对定位的元素居中?
div居中的方式:text-algin:center/margin:0 auto;
浮动元素居中的方式:position:relative
让绝对定位的元素居中: left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
怎样清除浮动?
- 1.clear:both 2.父级overflow:hidden 3.父级伪元素after:{content:””,clear:both;display:block}
如何实现中间div宽度固定,两端div自适应宽度?说出2种方法
- 双飞翼(自适应加个父级容器,应用margin),圣杯(不加容器,padding)
Background是否包含border?
CSS3的变形有哪些类型?
- Rotale,scale,skew,translate
说明CSS3动画(@keyframes,animation)和过渡(transition)的区别?
- 1)动画不需要事件触发,过渡需要。
- 2)过渡只有一组(两个:开始-结束) 关键帧,动画可以设置多个
CSS定位有哪些类型?说明一下具体的应用场合
- relative,自身为原点
- absolute,浏览器窗口原点为原点
- fixed,不随滚动条拖动 页面而滚动
优雅降级和渐进增强是什么意思?
渐进增强 progressive enhancement:针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验。
优雅降级 graceful degradation:一开始就构建完整的功能,然后再针对低版本浏览器进行兼容。
区别:优雅降级是从复杂的现状开始,并试图减少用户体验的供给,而渐进增强则是从一个非常基础的,能够起作用的版本开始,并不断扩充,以适应未来环境的需要。降级(功能衰减)意味着往回看;而渐进增强则意味着朝前看,同时保证其根基处于安全地带。
display样式值有哪些?有啥作用
- none,block,inline,inline-block
如何在sass中定义一个变量
- 声明变量的符号“$”
什么是弹性布局?
CSS的优先级如何定义
- 行内样式>内部样式>外部样式>默认样式>继承样式
描述一下盒子模型
- W3c和IE两种:都由content, padding,border, margin构成
- box-sizing:content-box/border-box
什么是outline属性,有什么作用?
- outline (轮廓)是绘制于元素周围的一条线,位于边框边缘的外围,可起到突出元素的作用
Position属性有哪些值?分别用在什么场景?
隐藏一个界面元素的方式你能想到哪些?分别有什么区别
- display:none
- 该方式让元素隐藏时,隐藏的元素不占空间,隐藏后将改变html原有样式。一旦父节点元素应用了display:none,父节点及其子孙节点元素全部不可见,而且无论其子孙元素如何不屈地挣扎都无济于事。
- 2. visibility:hidden
- 该方式让元素隐藏时,隐藏的元素还是占用原有位置,隐藏后不将改变html原有样式。但,如果该元素的子元素使用了visibility:visible的话,改子元素将不被隐藏。
- 3. opacity:0
- 该方式让元素隐藏时,隐藏的元素还是占用原有位置,隐藏后不将改变html原有样式。但,隐藏的元素所对应的事件,仍然可以触发。display属性不会出现过渡 即使添加了transition,opacity属性适合有过渡的元素使用.
对于web前端适配多端的模式,你有什么解决方案?
- 适配多端 = 响应式设计页面
A,button和input点击出现蓝色边框,如何去掉?
outline: none;
父元素 height:auto,子元素使用了浮动样式,编码使用伪元素清除浮动
.clearfix::after{
content: “”;
display: block;
clear: both;
}
父子div中,子div宽度不定,编码实现子元素在父元素中水平居中
- 弹性布局
- display: flex;
- align-items: center; /* 垂直居中 */
justify-content: center; /* 水平居中 */
写出适配移动端的meta代码
30个高质量免费电子书下载网站
1.ePUBee http://t.cn/RV7t6G1
应该是全球最大的免费电子书库,超过10万本书籍,50万个文件版本,总能找到你喜欢的适合阅读器的电子书文件。品类齐全,搜索功能强大,图书管理方便。
2.鸠摩搜书 http://t.cn/RG07GHI
一个强大的搜书神站,无论是什么类型的书籍,只要你知道书名,就可以轻松的搜到。页面简洁到没朋友。
3.Owllook http://t.cn/RoVRyCH
一个免费下载各类小说的搜索引擎, 页面清新简洁,无广告。直接输入书名即可搜索
4.我的小书屋 http://t.cn/RJA5USm
提供各类畅销小说,网络小说,以及一些原版书籍下载。不仅如此还支持kindle漫画书下载。
5.西林街 http://t.cn/RXnrXtr
垂直专注于网 盘、视频、文库(文档、古籍、专业书籍、电子书[PDF、ePub、Mobi等格式])、学术(各种期刊、论文、学报等)和Mooc(在线课程、学习、视频教程)等资源的搜索。
6.书伴(kindle必备) http://t.cn/R9t9wDH
书伴,原名“Kindle伴侣”,能让你更加深入地使用手中的Kindle阅读器,让读书成为生命的一部分,让灵魂永远行走在路上。
7.盘搜搜 http://t.cn/R0HoKpI
一个老牌网盘搜索工具,功能非常强大。非常简洁的界面。每天都有更新,不同达人分享自己的“盘中资源”!盘搜不存储任何网盘内容,无论是工作还是学习都必备。
8.众人搜索网 http://t.cn/RkZw0z6
一个电子书搜索功能的网站,可以同时搜索各类电子书、电子小说等。
9.计算机书籍控 http://t.cn/zR03zzO
这个网站可谓是计算机专业的福音,这里包含许多优秀的计算机书籍下载。无论是英文原版还是中文这里都有。
10.走读派 http://t.cn/zQDUssJ
走读派提供一站式免费Kindle电子书下载与推送服务,是最方便的Kindle电子书网站,为您的Kindle提供免费电子书资源。算是kindle必备的一个网站。
11.苦瓜书盘 http://t.cn/ROe1pLk
苦瓜书盘是供网友交流适合电纸书阅读的6寸pdf及mobi格式电子书制作技术的网站,这里提供电子书上传及下载。
12.书语者 http://t.cn/RKwcwDq
书语者电子图书馆,一个搜书网站,让你快速找到想要的书籍!
13.知轩藏书 http://t.cn/RvukMXT
这是一个小说网站,在这里你可以找到你想要的小说。
14.书享家电子书下载导航 http://t.cn/EhLaW9G
整合数十个相关网站,目前仍不断更新中。
15.集思会 http://t.cn/RoZWLBe
集思会是一个kindle推送网站,你可以把本地和在线的kindle图书推送到你的kindle中进行阅读,也可以分享你的kindle图书给更多需要看的人
16.万千集合站 http://t.cn/R3FVezU
包含非常多的教材类相关电子书籍,搜索后直接显示下载链接。搜索结果基本涵盖了所有版本的电子教材、习题详解等。
17.蚂蚁搜书 http://t.cn/EhLaW9y
IT书更多的资源网站,支持下载,不支持kindle推送。注册需要加微信索要邀请码,也是管理很严格了
18.影印古籍资料 http://t.cn/RTQSO8w
提供7000+种古籍资料的网上阅读和PDF格式下载服务。
19.书格 http://t.cn/RhxO8ot
书格是一个自由开放的在线古籍图书馆,致力于开放式分享、介绍、推荐古本(四九以前的影像本)PDF;网站致力于为古籍的保护与数字化传播贡献。
20.图灵社区 http://t.cn/R6tu1k7
图灵社区很出名了,尤其是IT相关的书籍。
21.云海电子书 http://t.cn/RxAwXin
分类比较多,也可以自定义搜索,下载走的是国内城通网盘通道,还是比较快的。
22.Freebooks http://t.cn/RAVSgrO
Free-eBooks是一个提供免费电子书下载,电子书资源,电子书作者介绍的网站,你可以免 费下载你喜欢的电子书,也可以上传你自己的电子书分享
23.古登堡计划 http://t.cn/hnhhU
古登堡计划(Project Gutenberg)是一个以自由的和电子化的形式,基于互联网,大量提供版权过期而进入公有领域书籍的一项协作计划。
24.Free Book Spot http://t.cn/SwB354
Free Book Spot是一个免 费英文电子书大全网站,它提供有4485本免 费电子书,分为96个分类,高达71.97GB 。
25.Freetechbook
http:www.freetechbooks.com/
Free Tech Books 也是一个提供科技类免费电子书下载的网站,该网站提供的电子书都是符合法律要求或是版权协议,许可免 费的。
26.Bookboon http://t.cn/RujLAHM 这也是国外一个比较有名的网站,收录了成千上万英文原著。
27.Manybooks http://t.cn/heT1k 同样是国外出色的电子书下载网站,包含各类英文原版书籍下载阅读。
28.古籍图书馆 http://t.cn/zO5uVMj 这个网站上都是一些比较古老的书籍,提供下载和阅读
29.汉川草庐 http://t.cn/hHM9G 是一个喜欢文史哲,住在台湾的中国人所建造的后花园。
30.国学网 http://t.cn/hVE7v一家在国学传播领域独具特色的文化创意企业,主要从事古籍数字化研究、网络文献检索开发和网站建设,是中国最大的专业古籍电子文献数据公司之一。
JavaScript数据类型和变量
数据类型
计算机顾名思义就是可以做数学计算的机器,因此,计算机程序理所当然地可以处理各种数值。但是,计算机能处理的远不止数值,还可以处理文本、图形、音频、视频、网页等各种各样的数据,不同的数据,需要定义不同的数据类型。在JavaScript中定义了以下几种数据类型:
Number
JavaScript不区分整数和浮点数,统一用Number表示,以下都是合法的Number类型:
1 | 123; // 整数123 |
计算机由于使用二进制,所以,有时候用十六进制表示整数比较方便,十六进制用0x前缀和0-9,a-f表示,例如:0xff00
,0xa5b4c3d2
,等等,它们和十进制表示的数值完全一样。
Number可以直接做四则运算,规则和数学一致:
1 | 1 + 2; // 3 |
注意%
是求余运算。
字符串
字符串是以单引号’或双引号”括起来的任意文本,比如'abc'
,"xyz"
等等。请注意,''
或""
本身只是一种表示方式,不是字符串的一部分,因此,字符串'abc'
只有a
,b
,c
这3个字符。
布尔值
布尔值和布尔代数的表示完全一致,一个布尔值只有true
、false
两种值,要么是true
,要么是false
,可以直接用true
、false
表示布尔值,也可以通过布尔运算计算出来:
1 | true; // 这是一个true值 |
&&
运算是与运算,只有所有都为true
,&&
运算结果才是true
:
1 | true && true; // 这个&&语句计算结果为true |
运算是或运算,只要其中有一个为true
,运算结果就是true
:
1 | false false; // 这个语句计算结果为false |
!
运算是非运算,它是一个单目运算符,把true
变成false
,false
变成true
:
1 | ! true; // 结果为false |
布尔值经常用在条件判断中,比如:
1 | var age = 15; |
比较运算符
当我们对Number做比较时,可以通过比较运算符得到一个布尔值:
1 | 2 > 5; // false |
实际上,JavaScript允许对任意数据类型做比较:
1 | false == 0; // true |
要特别注意相等运算符==
。JavaScript在设计时,有两种比较运算符:
第一种是==
比较,它会自动转换数据类型再比较,很多时候,会得到非常诡异的结果;
第二种是===
比较,它不会自动转换数据类型,如果数据类型不一致,返回false
,如果一致,再比较。
由于JavaScript这个设计缺陷,_不要_使用==
比较,始终坚持使用===
比较。
另一个例外是NaN
这个特殊的Number与所有其他值都不相等,包括它自己:
1 | NaN === NaN; // false |
唯一能判断NaN
的方法是通过isNaN()
函数:
1 | isNaN(NaN); // true |
最后要注意浮点数的相等比较:
1 | 1 / 3 === (1 - 2 / 3); // false |
这不是JavaScript的设计缺陷。浮点数在运算过程中会产生误差,因为计算机无法精确表示无限循环小数。要比较两个浮点数是否相等,只能计算它们之差的绝对值,看是否小于某个阈值:
1 | Math.abs(1 / 3 - (1 - 2 / 3)) < 0.0000001; // true |
null和undefined
null
表示一个“空”的值,它和0
以及空字符串''
不同,0
是一个数值,''
表示长度为0的字符串,而null
表示“空”。
在其他语言中,也有类似JavaScript的null
的表示,例如Java也用null
,Swift用nil
,Python用None
表示。但是,在JavaScript中,还有一个和null
类似的undefined
,它表示“未定义”。
JavaScript的设计者希望用null
表示一个空的值,而undefined
表示值未定义。事实证明,这并没有什么卵用,区分两者的意义不大。大多数情况下,我们都应该用null
。undefined
仅仅在判断函数参数是否传递的情况下有用。
数组
数组是一组按顺序排列的集合,集合的每个值称为元素。JavaScript的数组可以包括任意数据类型。例如:
1 | [1, 2, 3.14, 'Hello', null, true]; |
上述数组包含6个元素。数组用[]
表示,元素之间用,
分隔。
另一种创建数组的方法是通过Array()
函数实现:
1 | new Array(1, 2, 3); // 创建了数组[1, 2, 3] |
然而,出于代码的可读性考虑,强烈建议直接使用[]
。
数组的元素可以通过索引来访问。请注意,索引的起始值为0
:
1 | var arr = [1, 2, 3.14, 'Hello', null, true]; |
对象
JavaScript的对象是一组由键-值组成的无序集合,例如:
1 | var person = { |
JavaScript对象的键都是字符串类型,值可以是任意数据类型。上述person
对象一共定义了6个键值对,其中每个键又称为对象的属性,例如,person
的name
属性为'Bob'
,zipcode
属性为null
。
要获取一个对象的属性,我们用对象变量.属性名
的方式:
1 | person.name; // 'Bob' |
变量
变量的概念基本上和初中代数的方程变量是一致的,只是在计算机程序中,变量不仅可以是数字,还可以是任意数据类型。
变量在JavaScript中就是用一个变量名表示,变量名是大小写英文、数字、$
和_
的组合,且不能用数字开头。变量名也不能是JavaScript的关键字,如if
、while
等。申明一个变量用var
语句,比如:
1 | var a; // 申明了变量a,此时a的值为undefined |
变量名也可以用中文,但是,请不要给自己找麻烦。
在JavaScript中,使用等号=
对变量进行赋值。可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量,但是要注意只能用var
申明一次,例如:
1 | var a = 123; // a的值是整数123 |
这种变量本身类型不固定的语言称之为动态语言,与之对应的是静态语言。静态语言在定义变量时必须指定变量类型,如果赋值的时候类型不匹配,就会报错。例如Java是静态语言,赋值语句如下:
1 | int a = 123; // a是整数类型变量,类型用int申明 |
和静态语言相比,动态语言更灵活,就是这个原因。
请不要把赋值语句的等号等同于数学的等号。比如下面的代码:
1 | var x = 10; |
如果从数学上理解x = x + 2
那无论如何是不成立的,在程序中,赋值语句先计算右侧的表达式x + 2
,得到结果12
,再赋给变量x
。由于x
之前的值是10
,重新赋值后,x
的值变成12
。
要显示变量的内容,可以用console.log(x)
,打开Chrome的控制台就可以看到结果。
使用console.log()
代替alert()
的好处是可以避免弹出烦人的对话框。
strict模式
JavaScript在设计之初,为了方便初学者学习,并不强制要求用var
申明变量。这个设计错误带来了严重的后果:如果一个变量没有通过var
申明就被使用,那么该变量就自动被申明为全局变量:
1 | i = 10; // i现在是全局变量 |
在同一个页面的不同的JavaScript文件中,如果都不用var
申明,恰好都使用了变量i
,将造成变量i
互相影响,产生难以调试的错误结果。
使用var
申明的变量则不是全局变量,它的范围被限制在该变量被申明的函数体内(函数的概念将稍后讲解),同名变量在不同的函数体内互不冲突。
为了修补JavaScript这一严重设计缺陷,ECMA在后续规范中推出了strict模式,在strict模式下运行的JavaScript代码,强制通过var
申明变量,未使用var
申明变量就使用的,将导致运行错误。
启用strict模式的方法是在JavaScript代码的第一行写上:
1 | 'use strict'; |
这是一个字符串,不支持strict模式的浏览器会把它当做一个字符串语句执行,支持strict模式的浏览器将开启strict模式运行JavaScript。
不用var
申明的变量会被视为全局变量,为了避免这一缺陷,所有的JavaScript代码都应该使用strict模式。我们在后面编写的JavaScript代码将全部采用strict模式。
JavaScript基本语法
语法
JavaScript的语法和Java语言类似,每个语句以;
结束,语句块用{...}
。但是,JavaScript并不强制要求在每个语句的结尾加;
,浏览器中负责执行JavaScript代码的引擎会自动在每个语句的结尾补上;
。 让JavaScript引擎自动加分号在某些情况下会改变程序的语义,导致运行结果与期望不一致。在本教程中,我们不会省略;,所有语句都会添加;。
例如,下面的一行代码就是一个完整的赋值语句:
1 | var x = 1; |
下面的一行代码是一个字符串,但仍然可以视为一个完整的语句:
1 | 'Hello, world'; |
下面的一行代码包含两个语句,每个语句用;
表示语句结束:
1 | var x = 1; var y = 2; // 不建议一行写多个语句! |
语句块是一组语句的集合,例如,下面的代码先做了一个判断,如果判断成立,将执行{...}
中的所有语句:
1 | if (2 > 1) { |
注意花括号{...}
内的语句具有缩进,通常是4个空格。缩进不是JavaScript语法要求必须的,但缩进有助于我们理解代码的层次,所以编写代码时要遵守缩进规则。很多文本编辑器具有“自动缩进”的功能,可以帮助整理代码。
{...}
还可以嵌套,形成层级结构:
1 | if (2 > 1) { |
JavaScript本身对嵌套的层级没有限制,但是过多的嵌套无疑会大大增加看懂代码的难度。遇到这种情况,需要把部分代码抽出来,作为函数来调用,这样可以减少代码的复杂度。
注释
以//
开头直到行末的字符被视为行注释,注释是给开发人员看到,JavaScript引擎会自动忽略:
1 | // 这是一行注释 |
另一种块注释是用/*...*/
把多行字符包裹起来,把一大“块”视为一个注释:
1 | /* 从这里开始是块注释 |
大小写
请注意,JavaScript严格区分大小写,如果弄错了大小写,程序将报错或者运行不正常。
JavaScript基础
JavaScript代码可以直接嵌在网页的任何地方,不过通常我们都把JavaScript代码放到<head>
中:
1 | <html> |
由<script>...</script>
包含的代码就是JavaScript代码,它将直接被浏览器执行。
第二种方法是把JavaScript代码放到一个单独的.js
文件,然后在HTML中通过<script src="..."></script>
引入这个文件:
1 | <html> |
这样,/static/js/abc.js
就会被浏览器执行。
把JavaScript代码放入一个单独的.js
文件中更利于维护代码,并且多个页面可以各自引用同一份.js
文件。
可以在同一个页面中引入多个.js
文件,还可以在页面中多次编写<script> js代码... </script>
,浏览器按照顺序依次执行。
有些时候你会看到<script>
标签还设置了一个type
属性:
1 | <script type="text/javascript"> |
但这是没有必要的,因为默认的type
就是JavaScript,所以不必显式地把type
指定为JavaScript。
如何编写JavaScript
可以用任何文本编辑器来编写JavaScript代码。这里我们推荐以下几种文本编辑器:
Visual Studio Code
微软出的Visual Studio Code,可以看做迷你版Visual Studio,免费!跨平台!内置JavaScript支持,强烈推荐使用!
Sublime Text
Sublime Text是一个好用的文本编辑器,免费,但不注册会不定时弹出提示框。
Notepad++
Notepad++也是免费的文本编辑器,但仅限Windows下使用。
_注意_:不可以用Word或写字板来编写JavaScript或HTML,因为带格式的文本保存后不是_纯文本文件_,无法被浏览器正常读取。也尽量不要用记事本编写,它会自作聪明地在保存UTF-8格式文本时添加BOM头。
如何运行JavaScript
要让浏览器运行JavaScript,必须先有一个HTML页面,在HTML页面中引入JavaScript,然后,让浏览器加载该HTML页面,就可以执行JavaScript代码。
你也许会想,直接在我的硬盘上创建好HTML和JavaScript文件,然后用浏览器打开,不就可以看到效果了吗?
这种方式运行部分JavaScript代码没有问题,但由于浏览器的安全限制,以file://
开头的地址无法执行如联网等JavaScript代码,最终,你还是需要架设一个Web服务器,然后以http://
开头的地址来正常执行所有JavaScript代码。