2019-02-05 13 JavaScript基础语法

基础语法

  1. 数据类型
    • Undefined
    • Null
    • Boolean
    • String
    • Number
    • Object
    • Symbol
  2. HTML DOM
    • 修改HTML内容, document.getElementById(id).innerHTML="xxx"
    • 修改HTML属性, document.getElementById(id).attribute=new value
    • 修改HTML样式, document.getElementById(id).style.property = new style
    • 响应事件, onclick=JavaScript
    • 使用HTML DOM分配事件, document.getElementById(id).onCLick=function(){displayDate()}
  3. JS Window
    • Window
    • window.document.getElementById("header") - Screen
    • screen.availWidth可用屏幕宽
    • screen.availHeight可用屏幕高 - Location
    • window.location获取当前页面URL, 并重定向新的页面
    • location.href返回当前页面的URL - History
    • window.history包含浏览器历史
    • window.history.back()返回上一页
    • window.history.forward()向前加载页面 - Timing
    • setTimeout()未来时间执行代码
    • clearTimeout()取消setTimeout() - Cookies
    • cookie存储访问者的变量
    • document.cookie=xxx
  4. varletconst
    • let只有代码块有效..不支持变量提升…
    • var全局有效, 存在变量提升, 即先使用, 再说明, 如console.log(foo)//输出Undefined; var foo=2;
    • const声明只读常量, 块级作用域有效
  5. 变量的解构赋值, 即从数组、对象取值, 对变量进行赋值(用途)
    • 交换变量的值
let x=1;
let y = 2;
[x, y] = [y, x];
  • 从函数返回多个值
  • 函数参数的定义
  • 提取JSON数据
let jsonData = {
  status: "OK",
  data:[123, 456]
};
let {status, data:number} = jsonData;
  • 遍历Map结构
const map = new Map();
map.set('first', 'hello');

for(let [key, value] of map) {
  ...
}
  1. Symbol, 防止属性命名冲突, 为一种特殊数据类型
let s1 = Symbol();
let s2 = Symbol();

s1 === s2; //false

let s1 = Symbol('foo');
let s2 = Symbol('foo');

s1 === s2; //false
let mySymbol = Symbol();

let a{};
a[mySymbol] = 'Hello';
  1. Proxy, 用于修改操作的默认行为, 等同于在语言层面修改, 可以理解为在目标对象设置”拦截器”
var obj = new Proxy({}, {
  get: function (target, key, receiver) {
    console.log('getting${key}');
    return Reflect.get(target, key, receiver);
  }
});

++obj.count;
// getting count!
  1. Reflect
    • Object对象属于语言内部的方法, 放到Reflect
    • 修改Object方法给予返回结果(true, false)
    • Reflect对象的方法与Proxy对象的方法一一对应
 function Greeting(name) {
   this.name = name;
 }
 // new的写法
 const instance = new Greeting('张三');

 // Reflect#construct的写法
 const instance = Reflect.construct(Greeting, '张三');
  1. Promise(异步编程的解决方案, 保存未来结束的结果)
    • 状态
    • pending (进行中)
    • fulfilled (已成功)
    • rejected (已失败)
const getJSON = function(url) {
  const promise = new Promise(function(resolve, reject) {
    if(this.status === 200) {

    }
  });
  return promise;
};

getJSON("/post.json").then(function(json) {
  // do something
}, function(error) {
  // handle error...
});
  1. Iterator和for..of循环
let arr = ['a', 'b'];
let iter = arr[Symbol.iterator]();

iter.next(); // {value: 'a', done:false}
iter.next(); // {value: 'b', done:false}
iter.next(); // {value: Undefined, done:true}

  1. Generator函数 (提供异步编程解决方案, Generator函数是个状态机, 封装多个内部状态)
    • function关键字和函数名之间有个*
    • 函数体内部使用yield表达式, 定义不同状态
// yield类似暂停指令; Generator执行必须调用遍历器对象的`next`方法, 指针移到下一个状态, 即调用`next`, 内部指针从函数头部或上一次执行停止的地方开始, 直到`yield`或者`return`.
var arr = [1, [[2, 3], 4], [5, 6]];

var flat = function* (a) {
  var length = a.length;
  for(var i = 0; i < length; i++) {
      var item = a[i];
      if(typeof item != 'number') {
        yield* flat(item);
      } else {
        yield item;
      }
  };
};

for (var f of flat[arr]) {
  console.log(f);
}
// 1, 2, 3, 4, 5, 6
  • 异步
    • 协程 (多线程互相协作, 完成异步任务; 优势: 代码写法类似同步)
      • 协程A执行
      • 协程A执行到一半, 进入暂停, 执行权转移到协程B
      • 协程B交还执行权
      • 携程A恢复执行
var fetch = require('node-fetch');
function* gen() {
  var url = "https://api.github.com/users/github";
  var result = yield fetch(url); // 返回Promise对象
  console.log(result.bio);
};

var g = gen();
var result = g.next();

// Generator函数非常简介, 但是流程管理不方便(即何时执行第一阶段、何时执行第二阶段)
result.value.then(function(data) {
  return data.json();
}).then(function(data) {
  g.next(data);
});
  • Thunk函数 (支持”“传名调用”, 即参数等在执行时求值)
// ES5版本
var Thunk = function(fn) {
  return function() {
    var args = Array.prototype.slice.call(arguments);
    return function (callback) {
      args.push(callback);
      return fn.apply(this, args);
    };
  };
};

// ES6版本
const Thunk = function(fn) {
  return function(...args) {
    return function(callback) {
      return fn.call(this, ...args, callback);
    }
  };
};
  • Generator 函数的流程管理 (如果必须保证第一步执行完, 才能执行第二步, 则Thunk函数有用武之地)
  var fs = require('fs');
  var thunkify = require('thunkify');
  var readFileThunk = thunkify(fs.readFile);

  var gen = function* () {
    var r1 = yield readFileThunk('/etc/fstab');
    console.log(r1.toString());
    var r2 = yield readFileThunk('/etc/shells');
    console.log(r2.toString());
  };
  1. async 函数 (Generator函数的语法糖)
    • 内置执行器, async函数自带执行器, 不需要调用next()
    • 语义更清晰, asyncawait比星号和yield更清晰
    • 更广的适用性, async函数的await命令后面支持 Promise对象和原始类型的值
    • 返回值为Promise
const asyncReadFile = async function() {
  const f1 = await readFile('/etc/fstab');
  const f2 = await readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};
  1. Class (为了和C++/Java语法更接近)
// ES5, 生成实例对象的传统方法通过构造函数
function Point(x, y) {
  this.x = x;
  this.y = y;
}

// ES6
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
}
  1. 闭包 (当一个函数返回了一个函数, 且内部的局部变量还被新函数引用, 即为闭包)
    • 延时执行
    • 封装private成员变量
    • 简化多参数为单参数的函数, Math.pow(x, y)可简function make_pow(n) {return function(x) {return Math.pow(x, n)}}
// 外部代码无法访问变量x, 即闭包是携带状态的函数, 并且它的状态对外隐藏
'use strict';
function create_counter(initial) {
  var x = initial || 0;
  return {
    inc: function() {
      x += 1;
      return x;
    };
  };
};

var c1 = create_counter(10);
c1.inc();//11
c1.inc();//12
  1. 继承和原型链
    • 创建对象
 function Student(name) {
   this.name = name;
   // var tom 和 var lucy的hello()函数的内存地址都不一样, 如果要共享, 则
   // 需要调用Student.prototype.hello = ...;
   this.hello = function() {
     alert('Hello' + this.name + '!');
   }
 }
 var tom = new Student('tom'); // 必须使用 new, 否则即为普通函数

// tom的原型链为:
// tom -> Student.prototype -> Object.prototype -> null, 即如果tom.age找不到会一直往原型链往上找...
  • 原型继承 (继承实际是类型的扩展, JavaScript采用原型继承, 无法直接扩展Class, 因此需要修改原型链)
// 通过中间对象...
function PrimaryStudent(props) {
  Student.call(this, props);//调用Student的构造方法
  this.gradle = props.gradle || 1;
}

function F() {
}

F.prototype = Student.prototype;

// 修改PrimaryStudent.prototype为F, 即引用链调整为, PrimaryStudent.prototype -> new F -> Student.prototype
PrimaryStudent.prototype = new F();

// 修复PrimaryStudent原型的构造函数为PrimaryStudent...
PrimaryStudent.prototype.constructor = PrimaryStudent;

  1. 并发模型和事件循环

  2. 高阶函数 (函数作为参数, 即为高阶函数)

function add(x, y, f) {
  return f(x) + f(y);
}

对象

  1. keyprototype
  2. 创建对象, JavaScript使用{..}表示对象
let obj = {
  name: "object",
  son: {
    name: "son"
  }
}

// 支持动态加、减属性

obj.age = 18;// 新增age属性
delete obj.age;//删除age属性
let obj = Object.create({name:"obj"})
  1. 属性的可枚举性和遍历
let obj = {foo:123};
Object.getOwnPropertyDescriptor(obj, 'foo');
//
// {
//  value: 123,
//  writable: true,
//  enumerable: true,
//  configurable: true
// }
  1. 属性的遍历
    • for ... in, 返回对象自身和继承的可枚举属性(不含Symbol属性)
    • Object.keys(obj) 返回对象自身可枚举属性的键名数组(不含Symbol属性)
    • Object.getOwnPropertyNames(obj) 返回对象自身所有属性的键名数组(不含Symbol属性)
    • Object.getOwnPropertySymbols(obj) 返回对象自身所有Symbol属性的键名数组
    • Reflect.ownKeys(obj) 返回自身所有键名数组
  2. this关键字
    • 默认绑定, 不带任何修饰的函数调用
    • 隐式绑定, 绑定上下文对象
    • 显示绑定, 通过callapplybind显示调用绑定this
    • new绑定, new XXX()函数的构造调用
    • this绑定优先级, new绑定 > 显示绑定 > 隐式绑定 > 默认绑定
  3. super关键字(当前对象的原型对象)
const proto = {
  foo: 'hello'
};

const obj = {
  foo: 'world',
  find() {
    return super.foo; // 等同于Object.getPrototypeOf(this).foo(属性)
  }
}

Object.setPrototypeOf(obj, proto);
obj.find(); // hello

  1. 扩展运算符
    • 对象的扩展运算符..., 用于取出对象的所有可遍历属性, 拷贝到当前对象
let z = {a:3, b:4};
let n = {...z};

n // {a:3, b:4}

函数

  • 默认值
function log(x, y = 'World') {
  console.log(x, y);
}
log('Hello');
  • 解构赋值默认值结合使用
function f({x, y=5}) {
  console.log(x, y);
}
f({x:1}); // 1 5
  • 函数的length属性
(function (a) {}).length // 1
  • 作用域 (一旦设置参数的默认值, 函数进行声明初始化时, 参数形成单独的作用域(Context); 等初始化结束, 作用域就消失)
var x=1;
function foo(x, y = function() {x=2;}) { // 参数x在()作用域里, 因此执行y, x的赋值不生效
  var x = 3;
  y();
  console.log(x);
}

foo(); // 3
x // 1
  • 链式作用域(var声明的变量作用域为当前执行上下文(execution context), 即函数或全局作用域)
// 作用域链为 inner -> rainmain -> window
<script type="text/javascript">
  var rain = 1;
  function rainman() {
    function inner() {
      alert(rain);
    }
    inner();
  }
  rainman(); //输出1
</script>
  • 箭头函数
var f = v => v;

// 等同于
var f = function(v) {
  return v;
};
  • 尾调用优化 (调用帧(Call Frame), 即A调用B, 保存A帧, 等B结束回到A;尾调用相当于不需要保存A帧)
// 例子
function f() {
  let m = 1;
  let n = 2;
  return g(m+n);
}
f();

// 等同于
function f() {
  return g(3);
}
f();
  • rest参数
  function add(...values) {
    ...
  }
  add(1, 2, 3);

异步编程

  • 回调函数
fs.readFile('/etc/password', 'utf-8', function(err, data) {
  if(err) throw err;
  console.log(data);
});
  • 事件监听
  • 发布/订阅
  • Promise对象 (链式调用)
var readFile = require('fs-readfile-promise');

readFile(fileA)
.then(function(data) {
  console.log(data.toString());
})
.then(function () {
  return readFile(fileB);
})
.then(function (data) {
  console.log(data.toString());
})
.catch(function (err) {
  console.log(err);
});
  • Generator函数(ES6)

模块化(CommonJS, AMD, CMD, ES6)

  • CommonJS
    • module.exports.firstName=firstName, 导出/暴露函数或者变量
    • CommonJS模块即对象, 加载(查找)模块, 调用require('fs'), 运行时加载, 缺点是无法”静态优化”
  • ES6
    • export, 暴露变量、函数让外部模块访问
  // 写法1
  export var firstName = 'Michael';

  // 写法2
  var firstName = 'Michael';
  export {firstName};
  // 默认导出/暴露, 不需要导入
  export default firstName = 'Michael';
  • import, 加载外部模块
  import {firstName} from './profile.js'
  // 重新命名使用as关键字
  // 引用变量只读, 不允许修改
  import {firstName as name} from './profile.js'
  // 整体加载模块
  import * as profile from './profile.js'

2.15.4 参考文档