作者:yanyige | 发布时间:2017-04-10 08:49

第二天

一觉睡到第二天8点半,昨天真是太累了。简单洗漱完毕后出门。9点到达360公司,早餐可以自由选择,有自助餐或者馒头包子,可以满足各类人的需求。吃完饭我们就去培训室了。(据说今天有重要人士要来参观)

9时55分 第一节课 技术翻译 李松峰(Javascript高级程序设计翻译者)

翻译简史

《礼记·王制》

《越人歌》 春秋时期

汉至宋朝时期的佛经翻译

明、清时期的翻译

技术翻译和意义

翻译的标准

信达雅


忠实准确地传达原文内容


指译文通顺流畅


指译文有文采,文字典雅

翻译的流程

11时02分 第二节课 技术翻译

翻译的方法

14时00分 第三节课 用好你手中的JavaScript

理解需求

  1. 做什么?
  2. 为什么要做这个?
  3. 达到什么样的程度?
  4. 对未来变更的预判

理解需求

  1. DOM
  2. canvas
  3. SVG

结构、数据和 API

  1. Canvas 的渲染机制和分层
  2. 坐标和轨迹数据
  3. touch 行为和坐标转换
  4. 确定底层 API

Canvas 三图层结构

触屏相关

function getCanvasPoint(canvas, x, y){
  let rect = canvas.getBoundingClientRect();
  return {
    x: 2 * (x - rect.left), 
    y: 2 * (y - rect.top),
  };
}
circleCanvas.addEventListener('touchstart', handler);
circleCanvas.addEventListener('touchmove', handler);

……

document.addEventListener('touchend', done);

API 设计

API:外观+行为

API 设计:Recorder 行为

var recorder = new HandLock.Recorder({
  container: document.querySelector('#main')
});

function recorded(res){
  if(res.err){
    console.error(res.err);
    recorder.clearPath();
    if(res.err.message !== HandLock.Recorder.ERR_USER_CANCELED){
      recorder.record().then(recorded);
    }
  }else{
    console.log(res.records);
    recorder.record().then(recorded);
  }      
}

recorder.record().then(recorded);
API 设计:Locker 行为

使用 Locker 完成设置和验证动作

var password = '11121323';

var locker = new HandLock.Locker({
  container: document.querySelector('#handlock'),
  check: {
    checked: function(res){
      if(res.err){
        console.error(res.err); //密码错误或长度太短
      }else{
        console.log(`正确,密码是:${res.records}`);
      }
    },
  },
  update:{
    beforeRepeat: function(res){
      if(res.err){
        console.error(res.err); //密码长度太短
      }else{
        console.log(`密码初次输入完成,等待重复输入`);
      }
    },
    afterRepeat: function(res){
      if(res.err){
        console.error(res.err); //密码长度太短或者两次密码输入不一致
      }else{
        console.log(`密码更新完成,新密码是:${res.records}`);
      }
    },
  }
});

locker.check(password);
API 设计:外观
const defaultOptions = {
  container: null, //创建canvas的容器,如果不填,自动在 body 上创建覆盖全屏的层
  focusColor: '#e06555',  //当前选中的圆的颜色
  fgColor: '#d6dae5',     //未选中的圆的颜色
  bgColor: '#fff',        //canvas背景颜色
  n: 3, //圆点的数量: n x n
  innerRadius: 20,  //圆点的内半径
  outerRadius: 50,  //圆点的外半径,focus 的时候显示
  touchRadius: 70,  //判定touch事件的圆半径
  render: true,     //自动渲染
  customStyle: false, //自定义样式
  minPoints: 4,     //最小允许的点数
};

内部逻辑:check

async check(password){
    if(this.mode !== Locker.MODE_CHECK){
      await this.cancel();
      this.mode = Locker.MODE_CHECK;
    }  
    
    let checked = this.options.check.checked;
    
    let res = await this.record();
    
    if(res.err && res.err.message === Locker.ERR_USER_CANCELED){
      return Promise.resolve(res);
    }
    
    if(!res.err && password !== res.records){
      res.err = new Error(Locker.ERR_PASSWORD_MISMATCH)
    }
    
    checked.call(this, res);
    this.check(password);
    return Promise.resolve(res);
}

内部逻辑:update

async update(){
    if(this.mode !== Locker.MODE_UPDATE){
      await this.cancel();
      this.mode = Locker.MODE_UPDATE;
    }
    
    let beforeRepeat = this.options.update.beforeRepeat, 
        afterRepeat = this.options.update.afterRepeat;
    
    let first = await this.record();
    
    if(first.err && first.err.message === Locker.ERR_USER_CANCELED){
      return Promise.resolve(first);
    }
    
    if(first.err){
      this.update();
      beforeRepeat.call(this, first);
      return Promise.resolve(first);   
    }
    
    beforeRepeat.call(this, first);
    
    let second = await this.record();      
    
    if(second.err && second.err.message === Locker.ERR_USER_CANCELED){
      return Promise.resolve(second);
    }
    
    if(!second.err && first.records !== second.records){
      second.err = new Error(Locker.ERR_PASSWORD_MISMATCH);
    }
    
    this.update();
    afterRepeat.call(this, second);
    return Promise.resolve(second);
}

外部流程

var password = localStorage.getItem('handlock_passwd') || '11121323';

var locker = new HandLock.Locker({
  container: document.querySelector('#handlock'),
  check: {
    checked: function(res){
      locker.clearPath();
      if(res.err){
        if(res.err.message === HandLock.Locker.ERR_PASSWORD_MISMATCH){
          hint.innerHTML = '密码错误,请重新绘制!';
        }else{
          toast.className = 'show';
          setTimeout(function(){
            toast.className = 'hide';
          }, 1000);
        }
      }else{
        hint.innerHTML = '密码正确!';
      }
    },
  },
  update:{
    beforeRepeat: function(res){
      locker.clearPath();
      if(res.err){
        toast.className = 'show';
        setTimeout(function(){
          toast.className = 'hide';
        }, 1000);
      }else{
        hint.innerHTML = '请再次绘制相同图案';
      }
    },
    afterRepeat: function(res){
      locker.clearPath();
      if(res.err){
        if(res.err.message === HandLock.Locker.ERR_PASSWORD_MISMATCH){
          hint.innerHTML = '两次绘制的图形不一致,请重新绘制!';
        }else{
          toast.className = 'show';
          setTimeout(function(){
            toast.className = 'hide';
          }, 1000);
        }
      }else{
        hint.innerHTML = '密码更新成功!';
        password = res.records;
        localStorage.setItem('handlock_passwd', password);
        checkmode.click();
      }
    },
  }
});

selectMode.addEventListener('change', function(e){
  var value = e.target.value;
  if(value === 'update'){
    locker.clearPath();
    hint.innerHTML = '设置密码,请绘制密码图案';
    locker.update();
  }else if(value === 'check'){
    locker.clearPath();
    hint.innerHTML = '验证密码,请绘制密码图案';
    locker.check(password);
  }
});

var p = locker.check(password);

小结:流程设计

细节优化

16时01分 第四节课 JavaScript进阶教程

重新认识这门语言

变量声明与作用域
var i = 100;

(function(){
  console.log(i);
  for(var i = 0; i < 10; i++){
      //do sth.
  }
  console.log(i);
})();
let i = 100;

(function(){
  console.log(i);
  for(let i = 0; i < 10; i++){
      //do sth.
  }
  console.log(i);
})();
let i = 100;

(function(){
  console.log(i);
  let i = 0;
  for(; i < 10; i++){
      //do sth.
  }
  console.log(i);
})();

使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。

var a = 10; // let a = 10;

function a(){
  
}

console.log(a);
闭包
var list = document.querySelectorAll('ul>li');

for(var i = 0; i < list.length; i++){
  var item = list[i];
  item.onclick = function(){
    alert(item.innerHTML);
  }
}

解决方法:

var list = document.querySelectorAll('ul>li');

for(var i = 0; i < list.length; i++){
  (function(item){
    item.onclick = function(){
      alert(item.innerHTML);
    }
  })(list[i]);
}

or

var list = document.querySelectorAll('ul>li');

for(var i = 0; i < list.length; i++){
  var item = list[i];
  item.onclick = (function(item){
    alert(item.innerHTML);
  }).bind(null, item);
}
this

修复this的问题

可以使用箭头函数。

eg:

var obj = {
    value: 10,
    bar: function() {
        (function() {
            console.log(this.value);
        })();
    }
}

var obj = {
    value: 10,
    bar: function() {
        (() => {console.log(this.value)})();
    }
}
Prototype & Class

原型:描述物体与物体的“相似性”

类:对事物的归纳和分门别类

Class.prototype & proto
Prototype 使用中的常见问题
function Point(...axises){
  console.log('执行构造器');
  this.axises = axises;
}

Point.prototype[Symbol.toPrimitive] = function(hint){
  if (hint == "default" || hint == "number") {  
     return this.getLength();
  }  
  let axises = this.axises;
  return `(${axises})`;
}

Point.prototype.getDimension = function(){
  return this.axises.length;
}

Point.prototype.getLength = function(){
  return this.axises.reduce((x, y)=>Math.sqrt(x * x + y * y));
}

Point.prototype.sub = function(p){
  let axises = p.axises.map((x,i) => x - this.axises[i]);
  return this.getLength.apply({axises});
}

function Point2D(x = 0, y = 0){
  Point.call(this, x, y);
  Object.defineProperty(this, 'x', {
    get: () => this.axises[0],
    set: (x) => this.axises[0] = x
  });

  Object.defineProperty(this, 'y', {
    get: () => this.axises[1],
    set: (y) => this.axises[1] = y
  });
}

Point2D.prototype = new Point();

Point2D.prototype.circle = function(snap, r = 10, color = 'red'){
  return snap.circle(this.x, this.y, r).attr({
    fill: color
  });
}

var p1 = new Point2D(150, 200),
    p2 = new Point2D(300, 200);

console.log(p1+0, p1 instanceof Point);

const s = Snap("#panel svg");
var c1 = p1.circle(s, 10);
var c2 = p2.circle(s, 10, 'blue');

var p = new Point2D();

panel.addEventListener('mousemove', function(evt){
  p.x = evt.clientX;
  p.y = evt.clientY;

  if(Math.abs(p.sub(p1)) < Math.abs(p.sub(p2))){
    c1.animate({r: 20}, 500);
    c2.animate({r: 10}, 500);
  }else{
    c1.animate({r: 10}, 500);
    c2.animate({r: 20}, 500);
  }
});
var T = function(){};
T.prototype = Point.prototype;
Point2D.prototype = new T();

使用类继承来解决这种抽象

class Point{
  constructor(...axises){
    this.axises = axises;    
  }
  [Symbol.toPrimitive](hint){
    if (hint == "default" || hint == "number") {  
       return this.length;
    }  
    let axises = this.axises;
    return `(${axises})`;    
  }
  get dimension(){
    return this.axises.length; 
  }
  get length(){
    return this.axises.reduce((x, y)=>Math.sqrt(x * x + y * y));
  }
  sub(p){
    let axises = p.axises.map((x,i) => x - this.axises[i]);
    return (new Point(...axises)).length;    
  }
}

class Point2D extends Point{
  constructor(x, y){
    super(x, y);
  }
  get x(){
    return this.axises[0];
  }
  set x(value){
    this.axises[0] = value;
  }
  get y(){
    return this.axises[1];
  }
  set y(value){
    this.axises[1] = value;
  }
  circle(snap, r = 10, color = 'red'){
    return snap.circle(this.x, this.y, r).attr({
      fill: color
    });    
  }
}


var p1 = new Point2D(150, 200),
    p2 = new Point2D(300, 200);

const s = Snap("#panel svg");
var c1 = p1.circle(s, 10);
var c2 = p2.circle(s, 10, 'blue');

var p = new Point2D();

panel.addEventListener('mousemove', function(evt){
  p.x = evt.clientX;
  p.y = evt.clientY;

  if(Math.abs(p.sub(p1)) < Math.abs(p.sub(p2))){
    c1.animate({r: 20}, 500);
    c2.animate({r: 10}, 500);
  }else{
    c1.animate({r: 10}, 500);
    c2.animate({r: 20}, 500);
  }
});

直接覆盖原生方法的弊端

Array.prototype.remove = function(item){
  var idx = this.indexOf(item);
  if(idx >= 0){
    return this.splice(idx, 1);
  }
  return null;
}

var arr = [1,3,5,2,4];

console.log(arr.remove(3), arr);

for(var i in arr){
  if(typeof arr[i] === 'number') console.log(arr[i]);
  else console.log([i, '什么鬼?']);
}
过程抽象

过程抽象是一种特别的编程思路

数据抽象和过程抽象

DOM样式操作的“提纯”

function setStyle(el, key, value){
  el.style[key] = value;
}

let content1 = document.querySelector('.content:nth-child(2)');

setStyle(content1, 'color', 'red');
setStyle(content1, 'fontSize', '2em');


function setStyleProps(el, props){
  for(let key in props){
    setStyle(el, key, props[key]);
  }
}

let content2 = document.querySelector('.content:nth-child(4)');

console.log(content2);
setStyleProps(content2, {
  color: 'green',
  transform: 'rotate(30deg)',
  transformOrigin: '50% 50%',
  padding: '55px 0',
  textAlign: 'center',
});

let contents = document.querySelectorAll('.content:nth-child(2n+1)');

function setStylePropsAll(els, props){
  els.forEach(el => setStyleProps(el, props));
}

setStylePropsAll(contents, {
  color: 'blue',
  fontWeight: 'bold'
});

使用“纯的”函数变换

//允许函数最后两个参数用object map批量执行
function fold(fn){
  return function(...args){
    if(args.length > 0 && args.length < fn.length){
      let map = args[args.length - 1];
      if(map != null && typeof map === 'object'){
        args.pop();
        let ret = [];
        for(var key in map){
          ret.push(fn.apply(this, args.concat([key, map[key]])));
        }
        return ret;
      }
    }
    return fn.apply(this, args);
  }
}

//如果函数的第一个参数是一个列表,那么对这个列表的元素进行批量操作
function batch(fn){
  return function(subject, ...args){
    if(subject != null && typeof subject !== 'function'
      && subject.length >= 0){
      let ret = [];
      for(var i = 0; i < subject.length; i++){
        ret.push(fn.apply(this, [subject[i], ...args]));
      }
      return ret;
    }
    return fn.apply(this, [subject, ...args]);
  }
}

function join(str1, str2){
  return [str1, str2].join('!');
}

join = fold(join);

console.log(join({foo:'bar', err:'ok'}));

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

add = batch(add);
console.log(add(3, 4));

思考:纯函数能带来什么好处?

//允许函数最后两个参数用object map批量执行
function fold(fn){
  return function(...args){
    if(args.length > 0 && args.length < fn.length){
      let map = args[args.length - 1];
      if(map != null && typeof map === 'object'){
        args.pop();
        let ret = [];
        for(var key in map){
          ret.push(fn.apply(this, args.concat([key, map[key]])));
        }
        return ret;
      }
    }
    return fn.apply(this, args);
  }
}

//如果函数的第一个参数是一个列表,那么对这个列表的元素进行批量操作
function batch(fn){
  return function(subject, ...args){
    if(subject != null && typeof subject !== 'function'
      && subject.length >= 0){
      let ret = [];
      for(var i = 0; i < subject.length; i++){
        ret.push(fn.apply(this, [subject[i], ...args]));
      }
      return ret;
    }
    return fn.apply(this, [subject, ...args]);
  }
}

function setStyle(el, key, value){
  el.style[key] = value;
}
setStyle = batch(fold(setStyle));

let content1 = document.querySelector('.content:nth-child(2)');

setStyle(content1, 'color', 'red');
setStyle(content1, 'fontSize', '2em');

let content2 = document.querySelector('.content:nth-child(4)');

setStyle(content2, {
  color: 'green',
  transform: 'rotate(30deg)',
  transformOrigin: '50% 50%',
  padding: '55px 0',
  textAlign: 'center',
});

let contents = document.querySelectorAll('.content:nth-child(2n+1)');

setStyle(contents, {
  color: 'blue',
  fontWeight: 'bold'
});

函数变换来执行异步化操作

let red = document.querySelector('#traffic .red');
let yellow = document.querySelector('#traffic .yellow');
let green = document.querySelector('#traffic .green');

function turnOn(light){
  light.className += ' on';
}

function turnOff(light){
  light.className = light.className.replace(/on/g, '');
}

function delay(fn, time){
  return function(...args){
    return new Promise(resolve => {
      setTimeout(() => resolve(fn.apply(this, args)), 
                 time);
    });
  }
}

(async function(){
  //noprotect
  while(1){
    turnOn(red);
    await delay(turnOff, 2500)(red);
    turnOn(green);
    await delay(turnOff, 3000)(green);
    turnOn(yellow);
    await delay(turnOff, 1500)(yellow);
  }
})();

链式API

function batch(fn){
  return function(subject, ...args){
    if(subject != null && typeof subject !== 'function'
      && subject.length >= 0){
      let ret = [];
      for(var i = 0; i < subject.length; i++){
        ret.push(fn.apply(this, [subject[i], ...args]));
      }
      return ret;
    }
    return fn.apply(this, [subject, ...args]);
  }
}

function zip(props){
  function Class(result){
    this.result = result;
  }
  let keys = Object.keys(props);
  keys.forEach((key) => {
    Class.prototype[key] = function(...args){
      return props[key].apply(this, [this.result, ...args]);
    };
  });
  return (result)=>new Class(result);
}

function wrap(fn, Class){
  return function(...args){
    Class = Class || this.constructor;
    let ret = fn.apply(this, args);
    if(ret instanceof Class) return ret;
    return new Class(ret);
  }
}

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

function mul(x, y){
  return x * y;
}

function div(x, y){
  return x / y;
}

[add, mul, div] = batch(wrap)([add, mul, div]);

let N = zip({add, mul, div});

console.log(N(100).add(5).mul(2).div(5).result);