JavaScript

JavaScript #

基本语法 #

hasOwnProperty 与 in #

hasOwnProperty 检查属性是否对象本身的一个成员,此方法不检查原型链。 in 检查对象或其原型链中中是否有此属性。

function Test(){
  this.a = 'abc';
}
Test.prototype.b = 'efg';
var test = new Test;
alert(test.hasOwnProperty('a'));  //输出 true
alert(test.hasOwnProperty('b'));  //输出 false
alert('a' in test); //输出 true
alert('b' in test); //输出 true

let 与 const #

let 类似于 var,但是所声明的变量,只在 let 命令所在的代码块内有效,所以很适合作为 for 计数器。 const 则用于声明常量,指向的是地址。

作用域 #

JavaScript 的作用域是由函数决定的。 函数作用域的嵌套关系是由定义时决定的而不是调用时,所以 JavaScript 的作用域称为静态作用域,又叫词法作用域。

块作用域 #

ES6 有了块级作用域,所以自释放函数不再有必要

跨模块常量 #

导出常量

export const A = 1;

使用常量

import {A, B} from './constants';
import * as constants from './constants';

解构变量 #

var [a, b, c] = [1, 2, 3];
let [head, ...tail] = [1, 2, 3, 4];
var { bar, foo } = { foo: "aaa", bar: "bbb" };
var jsonData = {
    id: 42,
    status: "OK",
    data: [867, 5309]
}

let { id, status, data: number } = jsonData;

默认值

var [foo = true] = [];

函数结构+默认值

function move({x = 0, y = 0} = {}) {
    return [x, y];
}

move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]

工具 #

Babel #

用于将高版本的 js 转换为低版本的 js

安装 #

为了各项目的版本问题,尽量不要使用全局安装

npm install --save-dev babel-cli

使用 #

编译文件

babel <src_js> -o <target_js>

编译目录

babel <src_dir> -d <target_dir>

结合 gulp 实时编译 ES2015(ES6) #

  1. 安装 gulp-babel
npm install --save-dev gulp-babel
npm install --save-dev babel-core
npm install --save-dev babel-preset-es2015
npm install --save-dev babel-preset-stage-0
  1. 安装 gulp
npm install --global gulp gulp-cli
npm install --save-dev gulp
  1. 创建 .babelrc,指定规则
{
  "presets": [
    "es2015"
  ],
  "plugins": []
}
  1. 创建 gulpfile.js
var gulp = require('gulp');
var babel = require('gulp-babel');

const es6 = {
    src: 'src/*.js',
    dest: 'target_dir'
}

gulp.task('babel', function () {
    return gulp.src(es6.src)
        .pipe(babel())
        .pipe(gulp.dest(es6.dest))
});
gulp.task('watch', function () {
    gulp.watch(es6.src, ['babel']);
});
  1. package.json 创建任务
"scripts": {
    "foo":"gulp watch"
  }
  1. 运行 npm run watch 执行任务

Browserify #

Browserify 可以以 CommonJS 方式为浏览器端的 js 添加 require 特性。

安装 #

npm install -g browserify

使用 #

  1. 编写需要进行预编译的普通 js 文件。
  2. 编译为普通 js,语法:browserify core.js > bundle.js
  3. 在页面导入生成后的普通 js 文件。

ES 6 #

String #

新增方法

var s = 'Hello world!';

console.log(s.startsWith('Hello')); //  true
console.log(s.endsWith('!'));   //  true
console.log(s.includes('o'));   //  true

console.log('x'.repeat(3)); //  xxx

遍历字符串

for (let c of 'foo') {
    console.log(c);
}

多行字符串

console.log(`string text line 1
string text line 2`);

字符串模板

注意使用字符串模板使用 进行包裹。

var name = "Bob", time = "today";
console.log(`Hello ${name}, how are you ${time}?`);

数字 #

无穷判断

console.log(Number.isFinite(NaN));  //  false
console.log(Number.isFinite(Infinity)); //  false

NaN 判断

console.log(Number.isNaN(NaN)); //  true
console.log(Number.isNaN(15));   //  false

字符串转数字

//  ES 5
parseInt('12.34'); // 12
//  ES 6
Number.parseInt('12.34');    // 12

类型判断

注意在 js 中 25.025 是一样的

Number.isInteger(25);    // true
Number.isInteger(25.0);  // true
Number.isInteger(25.1);  // false

Math 工具方法

Math.trunc(-4.1);    // -4
Math.sign(-5);   // -1
Math.sign(5);    // +1
Math.sign(0);    // +0

array #

ArrayLike

只要对象有 length 属性,就可以通过 Array.from() 转换为真实的数组对象。

let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
};
let array = Array.from(arrayLike);
console.log(array.pop());   //  c
console.log(array.length);  //  2

let set = new Set(['a', 'b']);
console.log(Array.from(set));    // ['a', 'b']

Array.from() 还接收第二个参数实现 map() 功能。

let nums = [1, 2, 3];
console.log(Array.from(nums, n => n * 3));  //  [ 3, 6, 9 ]
// 以上相当于
console.log(Array.from(nums).map(n => n * 3));  //  [ 3, 6, 9 ]

Array.of() 用于将多个参数转为数组

console.log(Array.of(1, 2, 3).length);  //  3

Loop

for (let i of array.keys()) {
    console.log(i); //  0   1
}
for (let v of array) {
    console.log(v); //  a   b
}
for (let e of array.entries()) {
    console.log(e[0], e[1]);
}

其它方法

console.log([1, 2, 3, 4, 5, 6].filter(n => n % 2 == 0));    //  2   4   6
console.log([1, 2, 3, 4, 5, 6].find(n => n % 2 == 0));    //  2

[10, 'a'].forEach((x, i) => console.log(x, i)); // 1
[10, 'a'].every(x => x === 'a');  //  false
[10, 'a'].some(x => x === 'a');   //  true
[, 'a', undefined, null].join('#'); // "#a##"
[, 'a', undefined, null].toString(); // ",a,,"

正则表达式 #

var regex = new RegExp('xyz', 'i');
regex = /xyz/i;
//  ES 6 新支持
regex = new RegExp(/xyz/i);

Set #

var set = new Set();

[2, 3, 5, 4, 5, 2, 2].map(n => set.add(n));

set = new Set([1, 2, 3, 4, 4]);
console.log([...set]);  //  [ 1, 2, 3, 4 ]

set.add(10);
set.delete(3);
console.log(set.has(1));    //  true

//  Loop
for (let i of set) {
    console.log(i); //  1 2 4 10
}

Map #

ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是 “键” 的范围不限于字符串,各种类型的值(包括对象)都可以当作键。

let map = new Map();
map.set('x', 1);
map.set('y', 10);
console.log(map.get('x'));
console.log(map.has('y'));
map.delete('x');

let m = new Map().set('x', 10).set('foo', 'bar');
console.log([...m]);

//  Loop
for (let entry of map.entries()) {
    console.log(entry[0], entry[1]);
}

Iterator #

只要实现了 iterator 就可以用于 let..of 循环。

const colors = ['red', 'green', 'blue'];
let iterator = colors[Symbol.iterator]();

for (let v of iterator) {
    console.log(v); // red green blue
}

for (let i in colors) {
    console.log(i); // 0 1 2
}

for (let v of colors) {
    console.log(v); // red green blue
}

Object.keys(colors).forEach(function (key) {
    console.log(key);
});

Function #

剪头函数

var f1 = function (v) {
    return v;
};
var f2 = v => v;
console.log(f1('foo'));
console.log(f2('bar'));

对象 #

创建对象 #

var foo = {};
foo.name = 'foo';
foo.echo = function () {
  return 'foo';
}

使用对象初始化器创建对象 #

var foo = {
  name: 'foo',
  echo: function () {
    return 'foo';
  }
};

使用构造函数创建对象 #

定义构造函数

function Foo(name) {
  this.name = name;
  this.echo = function () {
    return 'foo';
  };
}

使用构造函数

var foo = new Foo('foo');

属性 #

属性的简写

ES6 允许在对象之中,只写属性名,不写属性值。这时,属性值等于属性名所代表的变量。

var foo = 'bar';
var bar = {foo};
console.log(bar);   //  { foo: 'bar' }
// 相当于
console.log({foo: foo});    //  { foo: 'bar' }

function f(x, y) {
    return {x, y};
}
console.log(f(1, 2));   //  { x: 1, y: 2 }

方法 #

方法的简写

var obj = {
    hello(){
        console.log('Hello!');
    }
};
var obj2 = {
    hello: function () {
        console.log('Hello!');
    }
};
obj.hello();
obj2.hello();

assgin #

Object.assign() 方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。

var target = {};
var source1 = {x: 1};
var source2 = {y: 10};
Object.assign(target, source1, source2);
console.log(target);

借助 assign() 方法可以完成对象的深复制。

function clone(source) {
    return Object.assign({}, source);
}
target = clone(source1);

call, apply, bind #

可以用 call 或 apply 方法改变函数上下文,但如果重复使用会不方便,因为每次都要把上下文对象作为参数传递,而且还会使代码变得不直观。针对这种情况,我们可以使用 bind 方法来永久地绑定函数的上下文。

func.bind(ctx, args...);

bind 还有一个重要的特性就是可以绑定参数列表

原型对象 #

原型对象指构造函数的 prototype 属性指向的对象。每个对象可以通过 prototype 扩展构造函数,也可以通过 __proto__ 属性访问其指向的原型,从任何对象沿着它开始遍历最终都可以追溯到 Object.prototype

Symbol #

ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。

创建 Symbol #

Symbol 值通过 Symbol 函数生成。

var s1 = Symbol();
var foo = Symbol("foo");

通过 Symbol.for(name) 创建的 Symbol 会放在池中,之后可以得到重用。

var s3 = Symbol('s3');
var s4 = Symbol('s4');
var s5 = Symbol.for('s4');
var s6 = Symbol.for('s4');
console.log(s3 === s4);  //  false
console.log(s5 === s4);  //  false
console.log(s5 === s6);  //  true

作为属性名 #

对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。

有三种实现方式

第一种

var obj1 = {};
obj1[foo] = 'one';

第二种

var obj2 = {
    [foo]: 'two'
};

第三种

var obj3 = {};
Object.defineProperty(obj3, foo, {value: 'three'});

其它操作 #

Symbol 作为属性名,该属性不会出现在 for...infor...of 循环中,需要通过 Object.getOwnPropertySymbols() 来获得。

for (let v in obj1) {
    console.log(v); //  bar
}

console.log(Object.getOwnPropertySymbols(obj1));    //  [ Symbol(foo) ]

Promise #

ES6 原生提供 Promise 功能。

var fs = require('fs');

var file = '../inputs/students.json';

var promise = new Promise(function (resolve, reject) {
    fs.readFile(file, function (err, res) {
        if (err)
            reject(err);
        else
            resolve(res);
    });
});

promise.then(function (res) {
    return JSON.parse(res);
}, function (err) {
    console.error(err)
    return err;
}).then(function (res) {
    console.log(res);
});

Generator #

Generator 是普通函数,但是 function 后跟着星号,且函数体内可以含有 yield 关键字。调用时返回迭代器而不是立即执行。

以下创建了三个状态

function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}

var hw = helloWorldGenerator();

hw.next() // { value: 'hello', done: false }
hw.next() // { value: 'world', done: false }
hw.next() // { value: 'ending', done: true }
hw.next() // { value: undefined, done: true }

yield 可以中断程序的运行,即执行到此将执行权让给其它函数。Generator 中不含有 yield 的话则相对于普通的惰性加载函数。 yield 可以包含参数,参数会作为 next 的返回值返回。 yield 用在表达式中需要添加括号,例:'foo' + (yield 2)

Generator 函数可以用在 for...of 循环中

function *foo() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
  yield 5;
  return 6;
}

for (let v of foo()) {
  console.log(v);
}

Generator 返回的迭代器对象可以抛出异常由 Generator 函数内部来处理

var g = function* () {
  try {
    yield;
  } catch (e) {
    console.log('内部捕获', e);
  }
};

var i = g();
i.next();

try {
  i.throw('a');
  i.throw('b');
} catch (e) {
  console.log('外部捕获', e);
}

Generator函数返回的遍历器对象,还有一个return方法,可以返回给定的值,并且终结遍历Generator函数。

function* gen() {
  yield 1;
  yield 2;
  yield 3;
}

var g = gen();

g.next()        // { value: 1, done: false }
g.return("foo") // { value: "foo", done: true }
g.next()        // { value: undefined, done: true }

yield*语句 用于在一个 Generator 函数中执行另一个 Generator 函数

function* bar() {
  yield 'x';
  yield* foo();
  yield 'y';
}

// 等同于
function* bar() {
  yield 'x';
  yield 'a';
  yield 'b';
  yield 'y';
}

如果yield命令后面跟的是一个遍历器对象,需要在yield命令后面加上星号,表明它返回的是一个遍历器对象。这被称为yield*语句。

异步操作同步化

function* loadUI() {
  showLoadingScreen();
  yield loadUIDataAsynchronously();
  hideLoadingScreen();
}
var loader = loadUI();
// 加载UI
loader.next()

// 卸载UI
loader.next()

Thunk 函数 #

Promise 只是将回调函数的横向加载,改成纵向加载,是回调函数的改进。

Thunk 函数

“求值策略”,即函数的参数到底应该何时求值。 一种意见是"传值调用"(call by value),即在进入函数体之前,参数表达式已经进行计算。 另一种意见是"传名调用"(call by name),将参数表达式作为整体传入。

编译器的"传名调用"实现,往往是将参数放到一个临时函数之中,再将这个临时函数传入函数体。这个临时函数就叫做 Thunk 函数。

function f(m){
  return m * 2;
}

f(x + 5);

// 等同于

var thunk = function () {
  return x + 5;
};

function f(thunk){
  return thunk() * 2;
}

JavaScript语言是传值调用,它的Thunk函数含义有所不同。在JavaScript语言中,Thunk函数替换的不是表达式,而是多参数函数,将其替换成单参数的版本,且只接受回调函数作为参数。

// 正常版本的readFile(多参数版本)
fs.readFile(fileName, callback);

// Thunk版本的readFile(单参数版本)
var readFileThunk = Thunk(fileName);
readFileThunk(callback);

var Thunk = function (fileName){
  return function (callback){
    return fs.readFile(fileName, callback);
  };
};

任何函数,只要参数有回调函数,就能写成Thunk函数的形式。

thunkify 模块

thunkify 模块可以将函数转换为 thunk 函数

var thunkify = require('thunkify');
var fs = require('fs');

var read = thunkify(fs.readFile);
read('package.json')(function(err, str){
  // ...
});

async 函数 #

async 函数就是 Generator 函数的语法糖。将Generator函数的星号 * 替换成async,将yield替换成await,仅此而已。

Generator 版本

var fs = require('fs');

var readFile = function (fileName){
  return new Promise(function (resolve, reject){
    fs.readFile(fileName, function(error, data){
      if (error) reject(error);
      resolve(data);
    });
  });
};

var gen = function* (){
  var f1 = yield readFile('/etc/fstab');
  var f2 = yield readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

async 版本

var asyncReadFile = async function (){
  var f1 = await readFile('/etc/fstab');
  var f2 = await readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

async 自带执行器,不用像 Generator 函数需要多次调用 next(),async 执行时会自动输出最后的结果。 async 返回值是 Promise,参数也是 Promise 对象,如果不是的话会被转换为 Promise 对象

async function getStockPriceByName(name) {
  var symbol = await getStockSymbol(name);
  var stockPrice = await getStockPrice(symbol);
  return stockPrice;
}

getStockPriceByName('goog').then(function (result) {
  console.log(result);
});

多种形式

// 函数声明
async function foo() {}

// 函数表达式
const foo = async function () {};

// 对象的方法
let obj = { async foo() {} }

// 箭头函数
const foo = async () => {};

注意:await命令后面的Promise对象,运行结果可能是rejected,所以最好把await命令放在try…catch代码块中。

async function myFunction() {
  try {
    await somethingThatReturnsAPromise();
  } catch (err) {
    console.log(err);
  }
}

// 另一种写法

async function myFunction() {
  await somethingThatReturnsAPromise().catch(function (err){
    console.log(err);
  };
}

Html 操作 #

innerHTML #

使用 DOMContentLoadedinsertAdjacentHTML 代替 document.write()innerHtml

document.addEventListener("DOMContentLoaded", function (e) {
    var date = new Date();
    var year = date.getFullYear();
    var month = date.getMonth() + 1;
    var day = date.getDate();
    document.body.innerHTML += "<p>today is " + year + "/" + month + "/" + day;
    <!--更高速-->
    document.body.insertAdjacentHTML("beforeend", "<p>today is " + year + "/" + month + "/" + day);
}, false)

前端工具总结 #

Browserify 打包 Node 模块的依赖环境,使其可以运行在浏览器中 PostCSS 使用 JS 来处理 CSS

参考资料 #

沪ICP备17055033号-2