精读JavaScript模式(一)
一、前言
为什么读这本书?
其实做前端开发,一个需求给不同工作经验的人去做,只要完工时间不算苛刻,大家都是能实现的。功能实现虽然大致相同,但当我们回归代码去看实现方式,代码书写的美观程度,以及实现的方法其实是不尽相同的。毕竟经验丰富的人,拿到一个需求,可能脑海里就浮现了多个可供选择的方案,而经验较浅的人,就更偏向于如何实现基本需求了。
例如说到过滤一个数组,第一想到使用for循环,不会想到filter方法;再如做条件判断,首先想到if else,忽略掉了还有which case或Boolean?true:false三元运算符之类的其它选择。说这些不是说后者毕竟比前者要好,毕竟对于不同的使用场景,合适的才是最佳的,但能举一反三,从三种甚至多种方法中做出选择,是肯定要比一招鲜要更好的。
经验的积累不是一天两天的事情,这点我也明白,那能不能先从基本做起,比如了解更好的代码书写规范,掌握好基本概念,知道一些实用的js模式甚至说套路,那这就是我读这本书的原因了。
从这本书,你会知道比常规for循环更优的写法,知道new一个构造函数时究竟发生了什么,知道为什么setTimeout('fun()',1000),setTimeout(fun,1000)两种写法,为什么前者加引号都能执行,知道更优秀的编码方式以及更多有趣的东西。
这个系列只是作为读书笔记,挑出一些重要或者我觉得有趣的的概念,如果觉得有趣,推荐阅读原书。
二、JavaScript概念
1.面向对象
JavaScript(以下简称js)是一门面向对象的编程语言,我们总说,万物皆对象,这点是没错的。但需要注意的是,js中的六种基本(原始)数据类型不是对象,它们分别是,String(字符串),Number(数字),Boolean(布尔类型),null,undefined,以及ES6新增的基本类型symbol。
复杂(引用)数据类型可以归纳为对象类型,而对象有两大类,本地对象与宿主对象。
宿主对象包含window和所有DOM对象,而本地对象包括了内置对象(如 Function,Array,Date)或自定义对象(var o = {});
基本数据类型与引用数据类型的区别在于,基本数据类型的变量名与值都是存放在栈内存中,而对于引用数据类型,变量在栈内存中,值存放中堆内存中,变量名指向由堆内存提供的值地址,不理解可以具体看看博主对于深浅拷贝中值的存放图解。
说到数据类型,null是需要单独说说的。我们可以在浏览器F12调出控制台,输入typeof null回车,可以看到输出为object,这是为什么呢?
在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此,null的类型标签也成为了 0,typeof null就错误的返回了"object"。
ECMAScript提出了一个修复(通过opt-in),但被拒绝。这将导致typeof null === 'object'。
这段话可以理解为,这是早期JS设计留下的缺陷,但我们只要记住,虽然typeof得到的是object类型,但null本质就是基本数据类型,那我们要判断null类型该怎么办呢,可以使用如下方法。
Object.prototype.toString.call(null) === [object Null]。
2.原型(prototype)
js中的继承是代码重用的一种方式,继承的方式很多,原型就是其中一种,需要注意的是,原型其实就是一个普通对象。我们创建的每一个函数其实都自带prototype属性,这个属性指向一个空对象,你可以为这空对象添加各种属性方法,而这些新增成员可以被其它对象继承,作为其它对象的自有属性。这个空对象也不是严格意义上的空,它自带一个constructo属性,它指向你新建的函数。
3.严格模式
严格模式是采用具有限制性JavaScript变体的一种方式,从而使代码显示地 脱离“马虎模式/稀松模式/懒散模式“(sloppy)模式。添加模式比较简单,你只需要在你希望执行严格模式的作用域添加"use strict"即可。
"use strict"
顾名思义,严格模式相比传统模式有以下改变:(笔试遇到过一次)
• 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
• 消除代码运行的一些不安全之处,保证代码运行的安全;
• 提高编译器效率,增加运行速度;
• 为未来新版本的Javascript做好铺垫。
二、高质量javaScript基本要点
1.编写可维护的代码
人人都喜欢开发新功能,一切从零开始,不喜欢维护旧代码,特别是一段段密密麻麻没有注释的代码,这点每个开发者都感同身受。但抛开阅读遗留代码或者同事的代码,就算是我们自己开发的功能,两三个月后回头再读也可能出现阅读困难的问题,这就导致了维护成本较高的问题;阅读代码超过开发功能的时间很明显是不合理的。
因此我们在开发新功能,或者维护旧代码的同时,就得花时间为不合理的代码进行调整,例如
• 可读的
• 一致的(看起来像同一个人写的,有统一的规范)
• 可预测的(能拓展)
• 有文档的(或注释)
2.减少全局对象
减少全局对象基本是每个前端者入行就被告知的点。js是使用函数来管理作用域(scope)的,那么可以说,在一个函数内定义的变量就是一个局部变量,局部变量在当前作用域外部是不可见的;反之,全局变量是不在任何函数体内声明的变量,或者是直接使用而未申明的变量。
function echo() {
var a = ;//局部
b = ;//虽然在函数体内,但是未使用var let之类申明。
};
var c = //全局,虽然有申明,但是在函数体外。
每个js运行环境都有一个隐式全局对象,通常浏览器用全局对象window代表这个全局对象隐式全局对象,我们创建的每个全局变量都是这个全局对象的属性,我们可以不在任何函数体内使用this就能查看这个全局对象的引用,如:
a = ;
console.log(a);//
console.log(this.a);//
console.log(window.a)//
console.log(window['a'])//
全局变量在js代码执行的整个作用域都是可见的,正因为它们存在于同一个命名空间中,所以会发生命名冲突的问题。我们很难保证自己定义的全局变量是否与三方库,插件中变量是否有重名,所以使用变量先去申明它是非常重要的。
顺带一提
function echo() {
var a = b = ;
}
其中b是全局变量,a是局部变量,等价于var a = (b = 0);(实际开发中肯定是不推荐这样的写法,可读性太差,只是书中有举例,顺带说说这种写法带来全局变量的问题)
隐式全局变量与显式全局变量
隐式全局变量:通过 var 创建的全局变量(在任何函数体之外创建的变量)不能被删除。
隐式全局变量:没有用 var 创建的隐式全局变量(不考虑函数内的情况)可以被删除。
var a = ;
b = ;
console.log(delete a)//false
console.log(delete b)//true
隐式全局变量并不算是真正的变量,可以说它们是全局对象的一个属性成员。而属性是可以通过delete运算符删除的,变量不可以被删除,这是两者的区别。
3.访问全局对象
我们在前面说,全局变量总是被隐性的添加为全局对象的属性,那么我们其实可以通过全局对象来访问全局变量,例如通过window。但并不是在所有的环境下默认隐性全局对象都是window,或者说某个环境的全局对象可能不叫window。但我们可以利用根据this指向原则始终能找到全局对象,
例如函数在自调情况下,this总是指向全局对象(严格模式下this会指向undefined)。
var global = (function (){
return this;
})();
console.log(global)//当前环境的全局对象
4.单 Var 模式
申明变量在编程中是高频率的,在函数顶部使用一个单独的var语句是非常推荐的一种模式。这么做有如下好处:
• 在同一个位置可以查找到函数所需的所有变量(变量集中,方便查找)
• 避免当在变量声明之前使用这个变量时产生的逻辑错误(申明提前的问题)
• 提醒你不要忘记声明变量,顺便减少潜在的全局变量
• 代码量更少(输入更少且更易做代码优化)
var a = ,
b = ,
c,
fun = function () {};
当然使用let const申明也是可以使用这种模式的,而且let申明变量也彻底解决了var申明提前这种较为诟病的问题,这里还是按照书中思路去整理了笔记,大家心里能明白就好。
5.申明提前:分散的var带来的问题
首先,申明提前可以说是var申明模式的一个隐性问题,有时候会带来一些不必要的麻烦,而在ES6中新增的let申明方式其实已经解决了var的申明提前问题,本来这一点可说可不说,但毕竟还是有一些面试题会说道,就简单带一带。
对于js来说,当我们在某个作用域(比如同一个函数内)里声明了一个变量,这个变量在整个作用域内都是可见的,可使用的,包括在 var 声明 语句之前,这种情况就是所谓的申明提前。
(function (){
console.log(a);//undefined
var a = echo;
console.log(a);//echo
})();
在这段代码中,尽管第一个console在变量a申明之前,它也不会报错,因为在这个函数体内,var a申明会提前(赋值不提前),任何一个地方,不管先后都能正确的使用它,它等同于
(function (){
var a;
console.log(a);//undefined
a = echo;
console.log(a);//echo
})();
6.更优的for 循环
这里不讨论for forEach while各类循环方法的性能优劣,毕竟循环之争一直存在,可读性,性能太多因素,还是根据实际使用场景来定夺,后面有空也确实想对于现有常用数据遍历可行方法进行一个整理。(应该不会鸽)
我们最常见的for循环写法
for (var i = ; i < arr.length; i++) {
//do something with arr[i];
}
在for 循环括号中,var i = 0其实只会申明一次,但i < arr.length 与i++是每次循环都会执行的。那么就存在一个问题,上面的代码每次循环都会重复取一次数组arr的length属性,这会降低代码的性能,特别是当arr不单单是个数组,而是一个HTMLCollection对象时。
HTMLCollection对象是由DOM方法返回的对象,例如:
• document.getElementsByName()
• document.getElementsByClassName()
• document.getElementsByTagName()
操作dom是一个很耗资源的行为,如果每次循环都要遍历查询dom元素显然不太合理,更好的做法是用变量一开始就保存数组的长度。
for (var i = , max = myarray.length; i < max; i++) {
// do something with myarray[i]
}
或者这样,将变量的申明统一在一起,for只用管好自己的循环。
var i = ,
myarray = [],
max = myarray.length;
for (; i < max; i++) {
// do something with myarray[i]
}
注意括号中的第一个分号我有保留,或者写成(i < max; i++;)也可以,分号不能丢,不然会报错。
通过上面的改写,不管循环多少次,其实都只用查询一次DOM节点的length,是不是比较nice。
对于for循环,其实还可以做少量的改进,在for中我们之所以申明i = 0的作用是告诉循环,i是从0开始递增并与max做判断是否需要继续下次循环,其实我们可以直接获取数组长度让其递减,效果是一样的。
var myarray = [],
i = myarray.length
for (; i --;){
// do something with myarray[i]
}
这样写分号总觉得有点奇怪,我们也可以使用while来进行代替
var myarray = [],
i = myarray.length;
while (i--) { //在某篇博客看到过while 比 for更快的说法
// do something with myarray[i]
}
这么做相比前面的写法有两个有点,第一,变量减少了,我们直接让将length赋予给i进行递减,省去了变量max,其次,递减到0的做法速度会更快,因为与零相比要比和非零数字或者数组长度比较要高效跟多。
第一篇就先记到这里,再写下去篇幅就太长了点,看着就不太想想读了,不过估计也没人会耐着性子读这样的文章吧。
第二篇也会抓紧时间写,倘若有人阅读过,也欢迎指出错误。
精读JavaScript模式(一)的更多相关文章
- 精读JavaScript模式(七),命名空间模式,私有成员与静态成员
一.前言 惰性十足,这篇2月19号就开始写了,拖到了现在,就是不愿意花时间把看过的东西整理一下,其它的任何事都比写博客要有吸引力,我要反省自己. 从这篇开始,是关于JS对象创建模式的探讨,JS语言简单 ...
- 精读JavaScript模式(二)
我在想知识点怎么去分类,原本计划一章节一篇,但这样会会显得长短不一.更主要的是看到哪写的哪更为随意.那么这一篇还是紧接第一篇进行知识梳理,上篇说到了更优化的for循环,现在继续聊聊其它的循环方式. 1 ...
- 精读JavaScript模式(八),JS类式继承
一.前言 这篇开始主要介绍代码复用模式(原书中的第六章),任何一位有理想的开发者都不愿意将同样的逻辑代码重写多次,复用也是提升自己开发能力中重要的一环,所以本篇也将从“继承”开始,聊聊开发中的各种代码 ...
- 精读JavaScript模式(三),new一个构造函数居然发生了什么?
一.前言 上个月底,爸爸因为事故突然离世,说心里话,现在看到'去世','爸爸'这样的字眼,眼泪都会忍不住在眼眶打转,还是需要时间治愈.最近也只是零碎的看了下东西,始终沉不下心去读书,直到今天还是决定捡 ...
- 精读JavaScript模式(六),Memoization模式与函数柯里化的应用
假期就这么结束了!十天假就有三天在路上,真的难受!想想假期除了看了两场电影貌似也没做什么深刻印象的事情.流浪地球,特效还是很赞,不过对于感情的描写还是逃不掉拖沓和尴尬的通病,对于国产科幻还是抱有支持的 ...
- 精读JavaScript模式(五),函数的回调、闭包与重写模式
一.前言 今天地铁上,看到很多拖着行李箱的路人,想回家了. 在上篇博客结尾,记录到了函数的几种创建方式,简单说了下创建差异,以及不同浏览器对于name属性的支持,这篇博客将从第四章函数的回调模式说起. ...
- 精读JavaScript模式(四),数组,对象与函数的几种创建方式
一.前言 放了个元旦,休息了三天,加上春运抢票一系列事情的冲击,我感觉我的心已经飞了.确实应该收收心,之前计划的学习任务也严重脱节了:我恨不得打死我自己. 在上篇博客中,笔记记录到了关于构造函数方面的 ...
- javascript 模式(1)——代码复用
程序的开发离不开代码的复用,通过代码复用可以减少开发和维护成本,在谈及代码复用的时候,会首先想到继承性,但继承并不是解决代码复用的唯一方式,还有其他的复用模式比如对象组合.本节将会讲解多种继承模式以实 ...
- 【读书笔记】读《JavaScript模式》 - 函数复用模式之现代继承模式
现代继承模式可表述为:其他任何不需要以类的方式考虑得模式. 现代继承方式#1 —— 原型继承之无类继承模式 function object(o) { function F() {}; F.protot ...
随机推荐
- codeforce 489d bfs分层处理
这个题确实没想到用bfs进行分层处理,后来看到了大佬的题解之后才想到了这一点 bfs dfs早就学了,可是还是不大会应用到这上面 可以分为三层,起始点,中间点,尾点,需要的数据是中间点到尾点的访问次数 ...
- mac系统下安装Windows(7,8,10都一样的步骤)
1.下载纯净版window10镜像文件(ISO) 注意:必须是纯净版,不是ghost版 2.打开bootcamp软件(位置:launchpad-其他) 注意:硬盘不要分区,若分过区,请合并. ...
- [javascript] javascript 实现数据滚动加载
// tpl generate var tmpl = (function (cache, $) { return function (str, data) { var fn = !/\s/.test( ...
- 用XPath查找HTML节点或元素
更新版以后会在我的新博客更新,请您移步 https://blog.clso.fun/posts/2019-03-03/46.html 虽然JQ和JS都能很方便的查找包含了ID及类名的元素,但某些情况下 ...
- .NET Core 跨平台 串口通讯 ,Windows/Linux 串口通讯,flyfire.CustomSerialPort 的使用
目录 1,前言 2,安装虚拟串口软件 3,新建项目,加入 flyfire.CustomSerialPort 4,flyfire.CustomSerialPort 说明 5,开始使用 flyfire.C ...
- TmsTimeUtils 时间戳
package com.sprucetec.tms.utils; import java.math.BigDecimal;import java.text.DateFormat;import java ...
- Git-工作区和暂存区的概念
工作区(Working Directory):就是在电脑里能看到的目录,如testcase文件夹就是一个工作区. 版本库(Repository):工作区有一个隐藏目录.git,是Git的版本库. ...
- C#6.0语言规范(十二) 数组
数组是一种数据结构,包含许多通过计算索引访问的变量.包含在数组中的变量(也称为数组的元素)都是相同的类型,这种类型称为数组的元素类型. 数组具有确定与每个数组元素相关联的索引数的等级.数组的等级也称为 ...
- 【Spark调优】:RDD持久化策略
[场景] Spark对RDD执行一系列算子操作时,都会重新从头到尾计算一遍.如果中间结果RDD后续需要被被调用多次,可以显式调用 cache()和 persist(),以告知 Spark,临时保存之前 ...
- [JavaScript] 的异步编程之手写一个Gernerator的例子
<html> <head> <meta charset="UTF-8"> <title>Generator Demo</tit ...