编写高质量代码


本文为《编写高质量代码》读书笔记

01.警惕Unicode乱码

要点

  • 在JS中可以使用中文来命名变量或函数名

  • ECMA标准规定JS语言基于Unicode标准进行开发,JS内核完全采用UCS字符集进行编写,因此在JS代码中每个字符都采用两个字节来表示。

在这里插入图片描述

  • Unicode把一对字符视为一个单一的字符,而JS认为一对字符是两个不同的字符,这将会带来很多问题,考虑到代码的安全性,我们应该尽量使用基本字符进行编码

名词解释

  • Unicode标准:一种字符集标准,用于对世界上不同语言、文字系统和符号进行编号和定义

  • UCS字符集:Unicode字符集简称UCS

资料

http://www.ruanyifeng.com/blog/2014/12/unicode.html

02.正确辨析JS句法中的词、句和段

词语是JS句法结构中的最小语义单位,包括指令、变量、常量、运算符等。

在JS中,词语之间必须使用分隔符进行分隔,否则JS会错误解析。

在这里插入图片描述

03.减少全局变量污染

  • 全局变量就是在所有作用域中都可见的变量。

  • 一个全局变量可以被程序的任何部分在任意时间改变,就使得程序的行为被极大地复杂化,在程序中使用全局变量降低了程序的可靠性。

04.注意JS数据类型的特殊性

防止浮点数溢出

  • 二进制的浮点数不能正确地处理十进制的小数,因此0.1+0.2不等于0.3。根本原因是JS使用Number类来表示数字(没有区分int和float)。遵循IEEE 754标准,通过64位来表示一个数字(1 + 11 +52)。

  • 1 符号位,0表示正数,1表示负数s

  • 11 指数位(e)

  • 52 尾数,小数部分(即有数字)

在0.1+0.2时,会将0.1和0.2转化为二进制,而转化过程中尾数会发生无限循环,然后进行对阶运算,js引擎对二进制进行截断,所以造成精度丢失。
在这里插入图片描述

  • 而浮点数的整数运算是精确的,所以可以用相加来处理。
    在这里插入图片描述

慎用JS类型自动转化

  • JS一般遵循:如果某个类型的值被用于需要其他类型的值的环境中,JS就自动将这个值转换成所需要的类型。
值(value)字符串操作环境数字运算环境逻辑运算环境对象操作环境
undefined“undefined”NaNfalseError
null“null”0falseError
非空字符串不转换字符串对应的数字值
NaNtrueString
空字符串不转化0falseString
0“0”不转化falseNumber
NaN“NaN”不转化falseNumber
Infinity“Infinity”不转化tureNumber
Number.POSITIVE_INFINITY(正无穷)“Infinity”不转化trueNumber
Number.NEGATIVE_INFINITY“-Infinity”不转化trueNumber
Number.MAX_VALUE“1.7976931348623157e+308”不转化trueNumber
Number.MIN_VALUE“5e-324”不转化trueNumber
其他所有数字“数字的字符串”不转化trueNumber
true“true”1不转化Boolean
false“false”0不转化Boolean
对象toString()valueOf()或 toString() 或 NaNtrue不转化

正确检测数据类型

typeof 任何变量,返回的始终都是以下6种类型之一:

  • “number”
  • “string”
  • “boolean”
  • “object”
  • “function”
  • “undefined”

另外,比较特殊的是,typeof null 返回 “object”。

检测类型的一般方法:

function type(T){
	return (T === null) ? "null" : (typeof o);
}
  • 但是typeof不能够检测复杂的数据类型,如正则表达式对象、日期对象等

  • 对于对象或数组,可以使用constructor属性,该值的引用是原来构造该对象的函数。

constructor判断类型表

值(value)typeof value(表达式返回值)value.constructor(构造函数的属性值)
var value = 1“number”Number
var value = ‘b’“string”String
var value = true“boolean”Boolean
var value = {}“object”Object
var value = new Object()“object”Object
var value = []“object”Array
var value = new Array()“object”Array
var value = function() {}“function”Function
function className() {}“object”className
使用constructor属性可以判断大部分数据的类型,
但是,对于undefined和null特殊值,就不能使用constructor,JS解析器会抛出异常。

那么对于null和undefined,如果要利用constructor属性,需这样判断:
const type = null && null.constructor; // null
const type = undefined && undefined.constructor; // undefined
对于数值直接量也不能直接使用constructor属性,
需要给数值加上一个小括号,原因是小括号运算符能够把数值转换为对象。

请添加图片描述

apply的妙用

  • 数组中的最大值
const arr = [1,23,123,1,4];

const a = Math.max.apply(null, arr);

console.log(a); // 123
  • 合并两个数组
const arr = [1,23,123,1,4];

const arr1 = ['t','n','s'];

Array.prototype.push.apply(arr, arr1);

console.log(arr);
/**
 * [
        1, 23,  123, 1,
        4, 't', 'n', 's'
    ]
*/

toString()检测对象类型是最安全的

  1. toString()方法的原理
    调用toString()方法把对象转为字符串,然后通过检测字符串中是否包含数组所特有的标志字符
    从而确定对象的类型。

安全检测JS基本数据类型和内置对象

/**
 * 获取基本数据类型和内置对象
 * @param {*} o 检测的值
 */
function typeOf(o) {
    const _toString = Object.prototype.toString;
    const _type = {
        "undefined": undefined,
        "number": "number",
        "boolean": "boolean",
        "string": "string",
        "[object Function]": "function",
        "[object Array]": "array",
        "[object RegExp]": "regexp",
        "[object Date]": "date",
        "[object Error]": "error",
    };
    return _type[typeof o] || _type[_toString.call(o)] || (o ? "object" : "null");
}
  • 对于非内置对象,该方法不适用,需要使用constructor和instanceof

避免误用parseInt

  • parseInt是一个将字符串转换为整数的函数。
parseInt("123abc"); // 123
parseInt("1.72"); // 1
parseInt(".123"); // NaN
  • parseInt(‘要转换的字符串’,‘该字符串的进制’)
parseInt('10',2); // 把二进制数字10转化为十进制整数2
parseInt('10',8); // 把八进制数字10转化为十进制整数8

05.防止JS自动插入分号

JS语言有一个机制:在解析时,能够在一句话后面自动插入一个分号。
如下:在return语句中自动插入分号将会导致返回为undefined的情况。

const f = function(){
	return
	{
		status: true
	};
}
  • 最佳实践

      在编写代码时,应养成使用分号结束的句子的良好习惯。
    

06.正确处理JS特殊值

正确使用NaN和Infinity

NaN是IEEE 754中定义的一个特殊的数量值,它不表示一个数字,尽管下面的表达式返回的是true。

typeof NaN === 'number'; // true

NaN与其它运算数的结果是NaN,并且NaN不等同于它自己。

  • isNaN(),辨别数字与NaN区别
  • 该全局 isFinite() 函数用来判断被传入的参数值是否为一个有限数值(finite number)。在必要情况下,参数会首先转为一个数值。

isNumber函数

const isNumber = function isNumber(value){
	return typeof value === 'number' && isFinite(value);
}

正确使用null和undefined

js有5种基本类型:String、Number、Boolean、null、Undefined。

null == undefined;
null !== undefined;
与null不同,undefined不是JavaScript的保留字,在ECMAScript v3标准中才定义undefined为全局变量,
初始值为undefined。
因此,在使用undefined值时就会存在一个兼容性问题(早期浏览器可能不支持undefined)
问题是:直接赋值和使用typeof运算符外,其他任何运算符对undefined的操作都会引发异常。

怎么判断浏览器是否支持undefined

var undefined;
console.log(undefined); // undefined

如果浏览器不支持undefined关键字,可以自定义undefined变量,并将其赋值为undefined。
例如:

var undefined = void null;

由于void在执行其后面的表达式时会忽略表达式的结果值,而总是返回值undefined
还可以:

var undefined = void 1;

或者:

var undefined = function(){}();
  • 可以使用typeof运算符来检测某个变量的值是否为undefined
var a;
if(typeof a == "undefined"){
}

使用假值

下面这些值的布尔值都是false,但是他们是不可互换的。

0
NaN
''
false
null
undefined

07.小心保留字的误用

JS定义了很多保留字,根据JS语法规则,保留字是不能用来命名变量或参数的。当保留字作为对象字面量的键值时,必须用引号括起来。因为保留字不能出现在对象点语法中,所以使用了保留字就需要使用括号表示法。
请添加图片描述

08.谨慎使用运算符

用===,而不用 ==

请添加图片描述
上面表达式如果全部使用 ===运算符,则都会返回true。

关于x === y返回值

若x和y类型相同,那么可以根据x的类型展开不同的判断:

  • 若x的类型是undefined或null,则返回true
  • 若x的类型是Number,只要x或y中有一个值为NaN,就返回false;若x和y的数字值相等,就返回true;若x或y中有一个是+0,另外一个是-0,返回true。
  • 若x的类型是String,当x和y的字符序列完全相同时返回true,否则返回false
  • 若x和y引用相同的对象时返回true,否则返回false
  • 若x的类型是boolean,当x和y同为true或false时返回true,否则返回false

关于x==y返回值

请添加图片描述

谨慎使用++和–

需要知道先执行递加运算,还是先进行赋值运算。

小心逗号运算符

逗号在JS中表示连续运算,并返回最后运算的结果。

const a = (1,2,3,4);
console.log(a); // 4

在JS的实际开发中,逗号运算符常与for循环语句联合使用。

for(var a =10, b= 0; a > b; a++,b+=2){
	//...
}

09.不要信任hasOwnProperty

hasOwnProperty方法是判断对象是否有属性,但是不会去原型上面找。

  • 考虑到hasOwnProperty是一个方法,而不是一个运算符,因此,它可能会被重新赋值。
    例如,下面的代码中,obj对象的hasOwnProperty成员被清空了,再使用hasOwnproperty判断obj的本地属性就会出现问题。
let obj = {};
obj.hasOwnProperty = null;
for(const name in obj) {
    if(obj.hasOwnProperty(name)){
        //...
    }
}

10.谨记对象非空特性

JS没有真正的空对象,因为每个对象都可以从原型链中取得成员。
比如:

const a = {};
console.log(a.constructor); // "function Object() {[native code]}"

避免出现这类问题,采用与处理for in问题相同的方法,加一个过滤条件;

if(typeof count[word] === 'number'){
	//...
}

11.慎重使用伪数组

JS没有真正的数组,因此typeof运算符不能辨别数组和对象,
判断是否为数组的方法:

function test(value) {
    console.log(value.constructor);
    if(value && typeof value === 'object' && value.constructor === Array) return true;
    return false;
}

console.log(test(arguments))
// [Function: Object]
// false

console.log(test([]));
// [Function: Array]
// true

12.避免使用with

with语句的语法如下:

with(expression){
	statement
}

with会把由expression计算出来的对象添加到当前执行上下文的作用域链的前面,然后使用这个扩大的作用域链来执行语句statement,最后恢复作用域链。不管其中的语句是否正常退出,作用域链都会被恢复。

由于with会把额外的对象添加到作用域链的前面,因此使用with可能会影响性能,并造成难以发现的错误。

13.养成优化表达式的思维方式

加小括号

使用小括号分隔符来优化表达式内部的逻辑层次,提高代码可阅读性。

  • bad case。可能被&&和||的优先级所迷惑。
(a + b > c && a - b < c || a > b > c)
  • good case。加入小括号,逻辑优先级的顺序就会非常清晰。
((a + b > c) && ((a - b < c) || (a > b > c)))

改变表达式结构顺序

随意混用大于号和小于号等运算符,会造成逻辑结构错乱。

  • bad case
if((age >= 6 && age < 18) || age >= 65){
}

采用统一的小于号,按照从小到大的思维顺序进行排列,阅读起来就非常清晰了。

  • good case
if((6 <= age && age < 18) || 65<= age){
}

避免布尔表达式的叠加

  • bad case
if(!(!isA || !isB)){
}
  • good case
(!isA || !isB) = !(isA && isB)
(!isA && !isB) = !(isA || isB)

if语句分解

对于使用?的复杂表达式,如果不仔细进行分析,很难理清他的逻辑顺序。

  • bad case
const a.b = new c(a.d ? a.e(1) : a.f(1));

使用if条件语句后,逻辑结构就变得非常清晰了

  • good case
if(a.d){
	var a.b = new c(a.e(1));
}else {
	var a.b = new c(a.f(0));
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值