【JavaScript高级程序设计】第22章(高级技巧)

22. 高级技巧

JavaScript是一种极其灵活的语言,具有多种使用风格。一般来说,要么使用过程方式,要么使用面向对象方式。
然而,由于其天生的动态属性,JavaScript还能使用更复杂和有趣的编程模式。

22.1 高级函数

  • 安全的类型检测
    • Object.prototype.toString.call() --> [object Array]
    • typeof & instanceof
  • 作用域安全的构造函数
    • 构造函数被当做普通函数调用, 非严格模式下this指向window
    • 构造函数内部进行类型检测判断
    • 继承时会出问题
      • this对象并非是Person实例,所以会创建一个新的Person对象返回
      • 返回值并没有被接受
      • 而且,this对象也没有被增强
function Person(name, age, job){
    console.log('this:' + Object.getPrototypeOf(this).constructor.name);
	if(this instanceof Person){
		this.name = name;
		this.age = age;
		this.job = job;
	}else{
		return new Person(name, age, job);
	}
}

function worker(name, age, job, salaries){
	Person.call(this,name, age, job);
	this.salaries = salaries;
}
worker.prototype = new Person();

不使用原型继承的话:

使用原型继承的话:

  • 惰性载入函数
    • 每次都做判断对性能浪费,实际上只需做一次就可
    • 惰性载入表示函数执行的分支仅会发生一次
    • 核心是:自我毁灭式更新
// 初始版本
function createXHR(){
	if(typeof XMLHttpRequest != "undefined"){
		return new XMLHttpRequest();
	}else if (typeof ActiveXObject != "undefined"){
		return new ActiveXObject();
	}else{
		throw new Error("No XHR object available");
	}
}
// 改进一,函数声明覆盖
function createXHR(){
	if(typeof XMLHttpRequest != "undefined"){
		createXHR = function(){
			return new XMLHttpRequest();
		}
	}else if (typeof ActiveXObject != "undefined"){
		createXHR = function(){
			return new ActiveXObject();
		}
	}else{
		createXHR = function(){
			throw new Error("No XHR object available");
		}
	}
	return createXHR();
}
// 改进二,函数表达式IIFE
var createXHR = (function(){
	if(typeof XMLHttpRequest != "undefined"){
		return function(){
			return new XMLHttpRequest();
		}
	}else if (typeof ActiveXObject != "undefined"){
		return function(){
			return new ActiveXObject();
		}
	}else{
		return function(){
			throw new Error("No XHR object available");
		}
	}
	return createXHR();
})();
  • 函数绑定
    • 方法函数被当做普通函数调用
    • 函数绑定要创建一个函数,可以在特定的this环境中以指定参数调用另一个函数
    • 该技巧常常和回调函数与事件处理程序一起使用
    • 作用: 在将函数当做变量传递的同时保留代码执行环境
// 方法调用
var handler = {
	message: "Event Handled",
	handleClick: function(event){
		console.log(this.message);
	}
}
// 传递方法,会改变this指向
button.addEventListener("click", handler.handleClick);
// 使用闭包改进
button.addEventListener("click", function(event){
	handler.handleClick(event); // 方法调用
})
// 自定义bind函数
function bind(_fun, _this){
	return function(){
		return _fun.apply(_this, arguments);
	}
}
// ES5 bind
button.addEventListener("click", handler.handleClick.bind(handler));
  • 函数柯里化
    • 与函数绑定紧密相关的话题是函数柯里化function currying,它用来创建已经设定好了一个或多个参数的函数。
// 概念理解
function add(a,b){
	return a + b;
}
function addFive(x){
	return add(5, x);
}
// 动态创建(柯里工厂)
function curry(fun){
	var args = Array.prototype.slice.call(arguments, 1); // fun参数
	return function(){
		var innerArgs = Array.prototype.slice.call(arguments); // 内部参数
		var finalArgs = args.concat(innerArgs); // 合并参数
		return fun.apply(null, finalArgs);
	}
}
// 绑定执行环境
function curry(fun, _this){
	var args = Array.prototype.slice.call(arguments, 2); // fun参数
	return function(){
		var innerArgs = Array.prototype.slice.call(arguments); // 内部参数
		var finalArgs = args.concat(innerArgs); // 合并参数
		return fun.apply(_this, finalArgs);
	}
}
// ES5 bind
var handler = {
	message: "Event Handled",
	handleClick: function(name, event){
		console.log(this.message + ":" + name + ":" + event.type);
	}
}
button.addEventListener("click", handler.handleClick.bind(handler, "mybtn"));

柯里化结果测试:

22.2 防篡改对象tamper-proof object

  • 不可扩展对象
    • 不能为对象本身添加新的属性,但可以给原型添加
    • Object.preventExtensions(obj) 静默失败
    • Object.isExtensible(obj) true/false
  • 密封对象
    • sealed object
    • 1.不可扩展; 2.[[Configurable]] == false(不能删除属性和方法)
    • Object.seal(obj)
    • Object.isSealed(obj)
  • 冻结对象
    • frozen object
    • 1.密封; 2.[[Writable]] == false (不能改变值)
    • Object.freeze(obj)
    • Object.isFrozen(obj)

三者是递进的,包含的,越来越严格的。从不能添新不能删旧,再到不能更改已有

22.3 高级定时器

  • 时间到了就把代码提交到任务队列,并不是立即执行
  • 使用链式setTimeout代替setInterval
  • yielding Processes
    • 数组分块array chunking --> 分割循环,防止阻塞,避免长时间运行脚本错误
  • 函数节流:某些代码不可以在没有间断的情况下连续重复执行
    • 场景:输入框change时查询匹配项
var processor = {
	timeoutId: null,
	performProcessing: function(){
		// 实际处理代码
	},
	process: function(){
		clearTimeout(this.timeoutId);
		var that = this;
		this.timeoutId = setTimeout(function(){
			that.performProcessing();
		}, 100)
	}
}
// 执行
processor.process();

// throttle节流
function throttle(method, _this){
	clearTimeout(method.tId);
	method.tId = setTimeout(function(){
		method.call(_this);
	})
}

22.4 自定义事件

事件是一种叫作观察者的设计模式,这是一种创建松散耦合代码的技术。
对象可以发布事件,用来表示对象生命周期中某个有趣的时刻到了。
然后其他对象可以观察该对象,等待有趣时刻的到来并通过运行代码来响应。
观察者模式有两类对象组成:主体观察者。主体负责发布事件,同时观察者通过订阅这些事件来观察该主体。
该模式的一个关键概念是主体并不知道观察者的任何事情,主体可以独自存在并正常运作,即使观察者不存在。

function EventTarget(){
	this.handlers = {};
}
EventTarget.prototype = {
	constructor: EventTarget,
	addHandler: function(type, handler){
		if(typeof this.handlers[type] == "undefined"){
			this.handlers[type] = [];
		}
		this.handlers[type].push(handler);
	},
	fire: function(event){
		if(!event.target){
			event.target = this;
		}
		if(this.handlers[event.type] instanceof Array){
			var handlers = this.handlers[event.type];
			for(let i = 0; i < handlers.length; i++){
				handlers[i](event);
			}
		}
	},
	removeHandler: function(type, handler){
		if(this.handlers[type] instanceof Array){
			var handlers = this.handlers[type], index = 0;
			for(;index < handlers.length; index ++){
				if(handlers[type] === handler){
					break;
				}
			}
			this.handlers[type].splice(index, 1);
		}
	}
}

function handleMessage(event){
	console.log("Message:" + event.message);
}
var target = new EventTarget();
// 订阅
target.addHandler("message", handleMessage);
// 发布
target.fire({type: "message", message: 'hello world'});

// 继承
function Person(name, age){
	EventTarget.call(this);
	this.name = name;
	this.age = age;
}
var prototype = Object.create(EventTarget.prototype);
prototype.constructor = Person;
Person.prototype = prototype;
Person.prototype.say = function(msg){
	this.fire({type: "message", message: msg});
}
// 实例
var p = new Person('zpj', 24);
p.addHandler("message", function(event){console.log(event.type + ":" + event.message)});
p.say("How are you?");

22.5 拖放

  • position: absolute; --> top/left
  • mousedown --> mousemove --> mouseup
  • 左上角 —> 鼠标捕捉点,看起来更平滑
var DragDrop = function(){
	// 模块模式,单例对象,闭包隐藏实现细节
	var dragdrop = new EventTarget(),
		dragging = null,
		diffX = 0,
		diffY = 0;
		
	function handleEvent(event){
		var target = event.target
		switch (event.type){
			case "mousedown":
				if(target.classList.contains("draggable")){
					dragging = target;
					diffX = event.clientX - target.offsetLeft;
					diffY = event.clientY - target.offsetTop;
					dragdrop.fire({type: "dragstart", target: dragging, x: event.clientX, y: event.clientY})
				}
				break;
			case "mousemove":
				if(dragging !== null){
					dragging.style.left = (event.clientX - diffX) + "px";
					dragging.style.top = (event.clientY - diffY) + "px";
					dragdrop.fire({type: "dragging", target: dragging, x: event.clientX, y: event.clientY})
				}
				break;
			case "mouseup":
				dragdrop.fire({type: "dragend", target: dragging, x: event.clientX, y: event.clientY})
				dragging = null;
				break;
		}
	}
	// 公共接口
	dragdrop.enable = function(){
		document.addEventListener("mousedown", handleEvent);
		document.addEventListener("mousemove", handleEvent);
		document.addEventListener("mouseup", handleEvent);
	}
	dragdrop.disable = function(){
		document.removeEventListener("mousedown", handleEvent);
		document.removeEventListener("mousemove", handleEvent);
		document.removeEventListener("mouseup", handleEvent);
	}
	return dragdrop;
}
// 实战
var div = document.createElement("div");
div.style.height = "50px";
div.style.width = "50px";
div.style.background = "red";
div.style.position = "absolute";
div.style.cursor = "grab";
div.classList.add("draggable");
document.body.appendChild(div);
var drag = DragDrop();
drag.enable();
全部评论

相关推荐

不愿透露姓名的神秘牛友
昨天 19:30
化身华黑&nbsp;今天询问对接人审批情况,结果被告知没HC了&nbsp;云计算&nbsp;
苦闷的柠檬精allin实习:主管面结束后hr每周保温一次,结果前几天和我说没hc了,我也化身华黑子了
投递华为等公司8个岗位 > 华为求职进展汇总
点赞 评论 收藏
分享
05-11 11:48
河南大学 Java
程序员牛肉:我是26届的双非。目前有两段实习经历,大三上去的美团,现在来字节了,做的是国际电商的营销业务。希望我的经历对你有用。 1.好好做你的CSDN,最好是直接转微信公众号。因为这本质上是一个很好的展示自己技术热情的证据。我当时也是烂大街项目(网盘+鱼皮的一个项目)+零实习去面试美团,但是当时我的CSDN阅读量超百万,微信公众号阅读量40万。面试的时候面试官就告诉我说觉得我对技术挺有激情的。可以看看我主页的美团面试面经。 因此花点时间好好做这个知识分享,最好是单拉出来搞一个板块。各大公司都极其看中知识落地的能力。 可以看看我的简历对于博客的描述。这个帖子里面有:https://www.nowcoder.com/discuss/745348200596324352?sourceSSR=users 2.实习经历有一些东西删除了,目前看来你的产出其实很少。有些内容其实很扯淡,最好不要保留。有一些点你可能觉得很牛逼,但是面试官眼里是减分的。 你还能负责数据库表的设计?这个公司得垃圾成啥样子,才能让一个实习生介入数据库表的设计,不要写这种东西。 一个公司的财务审批系统应该是很稳定的吧?为什么你去了才有RBAC权限设计?那这个公司之前是怎么处理权限分离的?这些东西看着都有点扯淡了。 还有就是使用Redis实现轻量级的消息队列?那为什么这一块不使用专业的MQ呢?为什么要使用redis,这些一定要清楚, 就目前看来,其实你的这个实习技术还不错。不要太焦虑。就是有一些内容有点虚了。可以考虑从PR中再投一点产出
点赞 评论 收藏
分享
避坑恶心到我了大家好,今天我想跟大家聊聊我在成都千子成智能科技有限公司(以下简称千子成)的求职经历,希望能给大家一些参考。千子成的母公司是“同创主悦”,主要经营各种产品,比如菜刀、POS机、电话卡等等。听起来是不是有点像地推销售公司?没错,就是那种类型的公司。我当时刚毕业,急需一份临时工作,所以在BOSS上看到了千子成的招聘信息。他们承诺无责底薪5000元,还包住宿,这吸引了我。面试的时候,HR也说了同样的话,感觉挺靠谱的。于是,我满怀期待地等待结果。结果出来后,我通过了面试,第二天就收到了试岗通知。试岗的内容就是地推销售,公司划定一个区域,然后你就得见人就问,问店铺、问路人,一直问到他们有意向为止。如果他们有兴趣,你就得摇同事帮忙推动,促进成交。说说一天的工作安排吧。工作时间是从早上8:30到晚上18:30。早上7点有人叫你起床,收拾后去公司,然后唱歌跳舞(销售公司都这样),7:55早课(类似宣誓),8:05同事间联系销售话术,8:15分享销售技巧,8:30经理训话。9:20左右从公司下市场,公交、地铁、自行车自费。到了市场大概10点左右,开始地推工作。中午吃饭时间大约是12:00,公司附近的路边盖饭面馆店自费AA,吃饭时间大约40分钟左右。吃完饭后继续地推工作,没有所谓的固定中午午休时间。下午6点下班后返回公司,不能直接下班,需要与同事交流话术,经理讲话洗脑。正常情况下9点下班。整个上班的一天中,早上到公司就是站着的,到晚上下班前都是站着。每天步数2万步以上。公司员工没有自己的工位,百来号人挤在一个20平方米的空间里听经理洗脑。白天就在市场上奔波,公司的投入成本几乎只有租金和工资,没有中央空调。早上2小时,晚上加班2小时,纯蒸桑拿。没有任何福利,节假日也没有3倍工资之类的。偶尔会有冲的酸梅汤和西瓜什么的。公司的晋升路径也很有意思:新人—组长—领队—主管—副经理—经理。要求是业绩和团队人数,类似传销模式,把人留下来。新人不能加微信、不能吐槽公司、不能有负面情绪、不能谈恋爱、不能说累。在公司没有任何坐的地方,不能依墙而坐。早上吃早饭在公司外面的安全通道,未到上班时间还会让你吃快些不能磨蹭。总之就是想榨干你。复试的时候,带你的师傅会给你营造一个钱多事少离家近的工作氛围,吹嘘工资有多高、还能吹自己毕业于好大学。然后让你早点来公司、无偿加班、抓住你可能不会走的心思进一步压榨你。总之,大家在找工作的时候一定要擦亮眼睛,避免踩坑!———来自网友
qq乃乃好喝到咩噗茶:不要做没有专业门槛的工作
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务