如何写好JavaScript是每一个前端工程师一直以来在思考的问题,月影老师告诉我们一些写好JavaScript的原则,同时也教了一些我们如何写好JavaScript的技巧,今天来继续跟着月影老师学JavaScript吧~~
起步
我们平时在刷题或者网上找资料的时候,得到的很多答案可能都不是用JavaScript实现的,我们就直接照搬翻译成JavaScript过来了,其实JavaScript有很多自己的特性,用的好的话直接起飞~
话不多说,我们来看案例
案例1:判断4的幂
菜鸟版
这题拿到太简单了,直接循环除以4最后能除尽就是啦
① 负数直接返回 false
② 对n进行循环除以4的遍历,只要有一次不能被4整除,就返 false
③ 否则返回 true
var isPowerOfFour = function(n) {
if( n < 1) return false
while(n > 1){
if(n % 4) return false
n /= 4
}
return true
};
看看LeetCode上执行的结果
还不错,够用了~
菜鸟学废版
我们使用位运算来提高效率啊
一个数x模4就相当于 x 的二进制形式 与 二进制数 11
也就是十进制的 3 按位相与,(与运算是两个数都为1结果才为1,否则结果都是0)因为11
前面都是0
,所以x的二进制形式最后只剩最后两位,最后两位 与 11
做与运算,结果还是自身(x的二进制形式的最后两位),所以 n % 4
和 n & 3
是一样的;
一个数除以4,相当于把他的二进制形式向右移动2位(向右移动一位相当于除以2),所以 n / 4
和 n >>> 2
是一样的
var isPowerOfFour = function(n) {
if( n < 1) return false
while(n > 1){
if(n & 3) return false
n >>>= 2
}
return true
};
看看LeetCode上执行的结果
进阶版
4的幂的前提是必须是2的幂,2的幂的前提必须是非负数
我们这里有三个判断,全部符合才是true
function isPowerOfFour(n) {
return n > 0 && (n & (n - 1)) === 0 && (n & 0xAAAAAAAA) === 0;
}
① n > 0
—— > 必须是非负数
② (n & (n - 1)) === 0
——> 必须是2的幂
n & (n - 1)
之前我们说过这样操作可以将 n 二进制表示的最低位 1 移除
我们再来看看2的幂数的二进制表示
1 0000 0001
2 0000 0010
4 0000 0100
8 0000 1000
16 0001 0000
32 0100 0000
64 1000 0000
所以满足 (n & (n - 1)) === 0
的数 都是 2 的幂
【注意】这里位元算外面的括号,由于位运算的优先级比较低,所以这个括号是不可省略的
③ (n & 0xAAAAAAAA) === 0
——> 在2的幂的基础上必须是4的幂
0xAAAAAAAA
也就是0b1010101010101010
,它的所有偶数二进制位都是 0,所有奇数二进制位都是 1。 这样一来,我们将 n 和 0xAAAAAAAA
进行按位与运算,如果结果为 0,说明 n 二进制表示中的 1 出现在偶数的位置,这样就排除了 2, 8, 32 之类的是2的幂不是4的幂的数字。
看看LeetCode上执行的结果
效率不咋地啊 不过多测几次还不一样
JS特性技巧版
都做到位运算了,为什么不更深层次的看看4的幂的数的二进制的特点呢? 1 0000 0001
4 0000 0100
16 0001 0000
64 0100 0000
4的幂的二进制都是1开头,后面偶数个零这样的形式
我们将n转换成2进制,然后 用 正则匹配结果作为返回值即可
function isPowerOfFour(n) {
n = n.toString(2)
return /^1(?:00)*$/.test(n)
}
看看LeetCode上执行的结果
还可以的样子啊~
案例2:深拷贝
平平无奇普通版
function deepClone(obj){
if (typeof obj !== 'object' || obj === null){
return obj
}
let result = Array.isArray(obj) ? []: {}
for (let key in obj) {
if(obj.hasOwnProperty(key)) {
result[key] = deepClone(obj[key])
}
}
return result
}
JS特性技巧版
我们用JSON这个API就很简单啦
function deepClone1(target) {
// 通过数据创建JSON格式的字符串
let str = JSON.stringify(target);
// 将 JSON 字符串创建为JS数据
let data = JSON.parse(str);
return data;
}
写成一行就是这样啦~
function deepClone1(target) {
return JSON.parse(JSON.stringify(target));
}
案例3:归并排序
《算法(第四版)》Java直译版
在《算法(第四版)》中关于归并排序的代码从Java翻译成JavaScript就是这样
/** * 排序数组 * @param {number[]} nums * @return {number[]} */
var sortArray = function (nums) {
const N = nums.length;
let temp = new Array();
mergeSort(nums, 0, N - 1, temp);
return nums;
};
/** * 归并排序 采用自顶向下的递归方法 * @param {number[]} nums * @param {number} left * @param {number} right * @param {number[]} temp * @returns */
var mergeSort = function (nums, left, right, temp) {
// 如果指针重叠了就返回
if (left >= right) {
return;
}
// let mid = Math.floor(left + (right - left) / 2);
let mid = (left + right) >> 1;
// 递归调用mergeSort
mergeSort(nums, left, mid, temp);
mergeSort(nums, mid + 1, right, temp);
// 归并两个有序数组
merge(nums, left, mid, right, temp);
};
/** * 归并两个有序数组 * @param {number[]} nums * @param {number} left * @param {number} mid * @param {number} right * @param {number[]} temp */
var merge = function (nums, left, mid, right, temp) {
// 将nums复制到temp中去
for (let k = left; k <= right; k++) {
temp[k] = nums[k];
}
// 给两个数组分别定义一个指针
let i = left;
let j = mid + 1;
// 将temp中的元素按规则写回nums
for (let k = left; k <= right; k++) {
if (i > mid) {
// 左半边取尽,取右半边元素
nums[k] = temp[j];
j++;
} else if (j > right) {
// 右半边取尽,取左半边元素,左指针右移
nums[k] = temp[i];
i++;
} else if (temp[i] <= temp[j]) {
// 谁小就取谁 ,左边小
nums[k] = temp[i];
i++;
} else {
// 右边小
nums[k] = temp[j];
j++;
}
}
};
其实在JavaScript数组有很多自带的方法,使得我们的数组可以直接当作队列或者栈才操作,用起来很方便
JS特性技巧版
使用JavaScript中数组的一些方法,使得归并排序实现起来异常简单,不用涉及过多的指针问题
更多内容我在之前的博文中有所介绍【算法】经典排序算法总结-JavaScript描述-图解-复杂度分析-插入-快速-归并-堆排序等
/** * 归并排序算法 * @param {*} arr 数组 * @returns 有序数组 */
function mergeSort(arr) {
// 采用自顶向下的递归方法
const N = arr.length;
if (N < 2) {
// 递归出口,数组只有一个元素,直接返回这个数组
return arr;
}
// x >> 1 是位运算中的右移运算,表示右移一位,等同于 x 除以 2 再取整,即 x >> 1 === Math.floor(x / 2)
// N >> 1 和 Math.floor(N / 2) 等价
let middle = N >> 1;
// 拆分为两个子数组
let left = arr.slice(0, middle);
let right = arr.slice(middle);
// 递归调用mergeSort
return merge(mergeSort(left), mergeSort(right));
}
/** * 对两个有序数组进行合并操作 * @param {*} left left数组 * @param {*} right right数组 * @returns 一个有序数组temp */
function merge(left, right) {
// 临时数组存储归并后的数据
const temp = [];
// 两个数组都还没有遍历结束
while (left.length && right.length) {
// 注意: 判断的条件是小于或等于,如果只是小于,那么排序将不稳定.
if (left[0] <= right[0]) {
// left[0]小,删除left数组中第一项left[0],并将它放入temp数组中
temp.push(left.shift());
} else {
// 删除right数组中第一项,并将它放入temp数组中
temp.push(right.shift());
}
}
// left数组还有元素,right数组遍历完了
while (left.length) {
// 将left数组剩下的元素都放入temp数组中
temp.push(left.shift());
}
// right数组还有元素,left数组遍历完了
while (right.length) {
temp.push(right.shift());
}
// 返回排序好的数组
return temp;
}
一些小技巧
转进制
将十进制数转换成各种进制
124.toString(12)
交换元素
let a = 1, b = 2
一般情况下是这样操作的
let temp = a
a = b
b = temp
JavaScript可以直接这样交换元素的值
[a, b] = [b ,a]
JavaScript的自有特性当然远远不止这些,后面我也会继续进行归纳总结的~
总结
- 一些位元算可以提高代码的执行效率,可以在数字的一些运算的时候考虑他们的二进制
- JavaScript中有很多好用的特性,我们要会利用好
- 后面我会再总结关于JavaScript位运算与小技巧的博文,敬请期待
更多相关博文
【青训营】月影老师告诉我写好JavaScript的三大原则——各司其责
【青训营】月影老师告诉我写好JavaScript的三大原则——组件封装
【青训营】月影老师告诉我写好JavaScript的三大原则——过程抽象
【青训营】月影老师告诉我写好JavaScript的四大技巧——风格优先
【青训营】月影老师告诉我写好JavaScript的四大技巧——保证正确
【青训营】月影老师告诉我写好JavaScript的四大技巧——封装函数
【青训营】月影老师告诉我写好JavaScript的四大技巧——妙用特性
也可以关注专栏: 【青训营笔记专栏】
今天的文章【青训营】月影老师告诉我写好JavaScript的四大技巧——妙用特性分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/20586.html