作者:yanyige | 发布时间:2017-04-10 08:49
第二天
一觉睡到第二天8点半,昨天真是太累了。简单洗漱完毕后出门。9点到达360公司,早餐可以自由选择,有自助餐或者馒头包子,可以满足各类人的需求。吃完饭我们就去培训室了。(据说今天有重要人士要来参观)
9时55分 第一节课 技术翻译 李松峰(Javascript高级程序设计翻译者)
翻译简史
《礼记·王制》
《越人歌》 春秋时期
汉至宋朝时期的佛经翻译
明、清时期的翻译
技术翻译和意义
- 翻译技术文档,掌握标准和工具
- 翻译技术文章,学习新技术
- 翻译技术图书,获得名声和报酬
翻译的标准
信达雅
信
忠实准确地传达原文内容
达
指译文通顺流畅
雅
指译文有文采,文字典雅
翻译的流程
11时02分 第二节课 技术翻译
翻译的方法
14时00分 第三节课 用好你手中的JavaScript
理解需求
- 做什么?
- 为什么要做这个?
- 达到什么样的程度?
- 对未来变更的预判
理解需求
- DOM
- canvas
- SVG
结构、数据和 API
- Canvas 的渲染机制和分层
- 坐标和轨迹数据
- touch 行为和坐标转换
- 确定底层 API
Canvas 三图层结构
触屏相关
- Touch 坐标到 Canvas 坐标的转换
function getCanvasPoint(canvas, x, y){
let rect = canvas.getBoundingClientRect();
return {
x: 2 * (x - rect.left),
y: 2 * (y - rect.top),
};
}
- 处理 Touch 事件
circleCanvas.addEventListener('touchstart', handler);
circleCanvas.addEventListener('touchmove', handler);
……
document.addEventListener('touchend', done);
API 设计
API:外观+行为
API 设计:Recorder 行为
- 使用 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);
小结:流程设计
- 流程应该如何设计?
- 流程与 API 的关系是什么?
细节优化
- Bug 和细节处理
this.container.addEventListener('touchmove', evt => evt.preventDefault(), {passive: false});
16时01分 第四节课 JavaScript进阶教程
重新认识这门语言
- 作用域和闭包
- 函数 this 的那些事儿
- Prototype 与 class
- 属性的高级定义方式
- 过程抽象与函数式编程
变量声明与作用域
- 以下代码的运行结果是?
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 使用中的常见问题
- 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);