目前的前端世界,三大框架横行,原生JavaScript
所用越来越少。但我认为JavaScript
作为每一个前端工程师的立身之本,学再多遍都不为过。
因此我决定整理JavaScript
中容易忽视或者混淆的知识点,写一系列文章,以灵魂拷问的方式,系统且完整的带大家遨游基础的的JavaScript
,给大家带来不一样的体验。
系列文章链接:
短路效应:&&
和||
都会发生短路
&&
只有在两个操作数都为 true
时,条件判断的结果才为 true
,如果操作数一为 false
,不会判断操作数二。||
两个操作数只要有一个为 true
,条件判断的结果就为 true
,因此操作数一为 true
时,不会判断操作数二。返回值规则
||
和 &&
首先会对操作数一执行条件判断,如果不是布尔值就先强制转换为布尔类型,然后再执行条件判断。||
来说,如果条件判断结果为 true
就返回第一个操作数的值,如果为 false
就返回第二个操作数的值。&&
则相反,如果条件判断结果为 true
就返回第二个操作数的值,如果为 false
就返回第一个操作数的值。||
和 &&
返回它们其中一个操作数的值,而非条件判断的结果(2<3)||(3<2)
返回值是多少?
1 + - + + + - + 1
结果是多少?+/-
号在 JavaScript
通常有三种用途:
++/--
: 自增自减,一元运算符+/-
: 正负,一元运算符上面表达式中没有涉及自增与自减的情况,一元运算符的优先级大于二元运算符,上述表达式执行顺序为:
1 + (- (+ (+ (+ (- (+ 1)))))) ----> 1 + 1 = 2
二进制的奇数最低位是1,偶数最低位是0,可以通过& 1运算后可以判断奇偶。
num & 1 === 1 // num 为奇数
num & 1 === 0 // num 为偶数
可以采用异或来实现两个变量值的交换
let a = 1
let b = 2
a ^= b
b ^= a
a ^= b
console.log(a) // 2
console.log(b) // 1
取整
~~num
请回答:y
的值为?
var x = 1;
var y = x + ++x + 3 * (x = ++x + x + x++ + 1)
+ x++ + 3;
console.log(y);
咱们来解剖一下这个长的要死的表达式:
第一个x值为1
第二个自增先加后用,x = 2
将 ++x + x + x++ + 1 的运算结果赋值给x
++x 先加后用,x = 3
x = 3
x++ 先用后加,x = 3
++x + x + x++ + 1 = 3 + 3 + 3 + 1 = 10
x++的后加,x = 4
将表达式的值赋给x,x由4变为10
x++ 先用后加,x = 10
此时所有的变量都已经求出
y = 1 + 2 + 3*10 + 10 + 3 = 46
x++,x最终值为11
new
运算符的有两种优先级吗?MDN
对 new
操作符的描述中,语法是:
new constructor[([arguments])]
([arguments])
意味着可以缺省,会存在 new constructor(...args)
和 new constructor
两种模式,并且前者的优先级高于后者。更详细的优先级见下图:
这个知识点非常重要,只有区分开了 new
带参列表和不带参列表,才能准确并且透彻的理解下面这道面试题。
function Foo(){
getName = function(){
console.log(1); };
return this;
}
Foo.getName = function(){
console.log(2); };
Foo.prototype.getName = function(){
console.log(3); };
var getName = function(){
console.log(4); };
function getName(){
console.log(5) };
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
toString()
的妙用吗?2-36
)// 参数为要转换的进制
var a = 10;
a.toString(2) // "1010"
a.toString(8) // "12"
a.toString(16) // "a"
利用 toString
的进制转换,可生成随机验证码
Math.random().toString(36).substring(3,7) //生成四位数的随机验证码
toString.call(()=>{
}) // [object Function]
toString.call({
}) // [object Object]
toString.call([]) // [object Array]
toString.call('') // [object String]
toString.call(22) // [object Number]
toString.call(undefined) // [object undefined]
toString.call(null) // [object null]
toString.call(new Date) // [object Date]
toString.call(Math) // [object Math]
toString.call(window) // [object Window]
原始值是没有属性也没有方法的,那为什么字符串可以调用方法那?
JavaScript
为了便于基本类型操作,提供了三个特殊的引用类型(包装类),即 Number,String,Boolean
,它们的 [[PrimitiveValue]]
属性存储它们的本身值。
光说这些有可能有些难理解,咱们来举个例子:
var str = 'zcxiaobao'
str2 = str.toUpperCase()
其实js引擎内部会这样处理:
var str = 'zcxiaobao'
// 调用方法,创建String的一个实例
new String(str)
// 调用实例上的方法,并将值返回
str2 = new String(str).toUpperCase()
// 销毁实例
但这里有一个需要注意的点,new String('1')
和 '1'
类型相同吗,我们来测试一下:
var str1 = new String('1');
var str2 = '1';
console.log(str1 === str2); // false
console.log(str1 == str2); // true
console.log(typeof(str1)) // object
// new String()得到的字符串为object格式
先看一个示例:
var str = '123456';
str.length = 0;
console.log(str, str.length); // 123456 6
很明显,修改 str.length
是无法做到修改字符串的长度的。
原因跟第二十二问是相同的,str
为原始值,调用 length
相当用 new String(str).length
,修改的是 new String(str)
的 length
,跟原始值 str
无关。
如果将上面代码修改一下,str
是由 new String
产生,修改 length
属性会生效吗?
var str = new String('123456');
str.length = 0;
console.log(str, str.length); // String {"123456"} 6
答案告诉我们,还是失败了。
二十三问中:new String()
生成的字符串是对象类型,为啥还不能使用 length
属性。那说明 length
属性,很有可能配置了不可写,测试一下上述猜想:
Object.getOwnPropertyDescriptor(str, 'length')
/*
{
value: 6,
writable: false,
enumerable: false,
configurable: false
}
*/
由控制台的打印可知:new String()
生成的字符串的 length
属性不止是不可写,而且是不可枚举、不可配置的。
千分符:每隔三位数加进一个逗号,也就是千位分隔符,以便更加容易认出数值。最经典的案例就是银行账单:
200,100,100.00
元。
toLocaleString()
方法var num = 1450068.12;
console.log(num.toLocaleString()) // 1,450,068.12
但这种方法经过测试,不管多少小数点后有多少位小数,都只会返回三位小数,并且经过四舍五入。
var num = 1450068.129999;
console.log(num.toLocaleString()) // 1,450,068.13
function addThousands(num) {
var result = '',
counter = 0;
[numInter, numDecimal] = (num || 0).toString().split('.');
// 倒序处理整数部分
for (var i = numInter.length - 1; i >= 0; i--) {
counter++;
result = numInter[i] + result;
if (!(counter % 3) && i != 0) {
result = ',' + result;
}
}
// 大家可以补充一下小数部分的代码
// 小数部分与整数部分类似
return result;
}
console.log(addThousands(42371582378423)) // 42,371,582,378,423
该方法实现太过惊天动地了,这里只留答案,剩下的留到正则部分再详细讲述。
function toThousands(num) {
var numStr = (num || 0).toString();
return numStr.replace(/(\d)(?=(?:\d{3})+$)/g, '$1,');
}
JSON.stringify
了吗?JSON.stringfy
的基本使用:JSON.stringify
可以将数组或者对象转化成 JSON
字符串。JSON.parse
可以将 JSON
字符串转化为数组或对象。基于这两个方法,可以产生很多用处,例如:
localStorage
只能存取字符串格式的内容,因此存之前转换成 JSON
字符串,取出来用时,在转化成数组或对象。JSON.stringfy
语法:
JSON.stringify(value[, replacer [, space]])
参数:
value
: 将要序列化成 一个 JSON
字符串的值。replacer
(可选):
JSON
字符串中;null
或者未提供,则对象所有的属性都会被序列化。space
:指定缩进用的空白字符串,用于美化输出特别要注意1、3、5条特性
symbol
为属性键的属性都会被完全忽略掉,即便 replacer
参数中强制指定包含了它们。NaN
和 Infinity
格式的数值及 null
都会被当做 null
。undefined
、任意的函数以及 symbol
值:
null
undefined
toJSON()
方法,该方法定义什么值将被序列化。Date
日期调用了 toJSON()
将其转换为了 string
字符串(同 Date.toISOString()
),因此会被当做字符串处理。Map/Set/WeakMap/WeakSet
,仅会序列化可枚举的属性