1.undefined如何出现
对于JavaScript,解释器在访问尚未初始化的变量或对象属性时返回undefined;函数没有返回值,是undefined
2.null如何出现
null
表示缺少的对象引用,JS本身不会将变量或对象属性设置为null;
一些原生方法,比如String.prototype.match()
,可以返回null
来表示丢失的对象。
3.常出现的错误:
TypeError: 'undefined' is not a function
TypeError: Cannot read property 'xxx' of undefined
type errors
4.undefined
1.)未为变量赋值时默认值为undefined
该标准明确定义,当访问未初始化的变量、不存在的对象属性、不存在的数组元素等时,将接收到一个undefined
的值
2.)Undefined type是其唯一值为undefined 值的类型
typeof undefined
返回“undefined”字符串
console.log(typeof undefined) //字符串undefined
console.log(typeof undefined ==="undefined") //true
let a;
typeof a=== "undefined"; // => true
3.)导致undefined的常见场景
(1).未初始化变量
myVariable
已声明,但尚未赋值,默认值为undefined
。
解决未初始化变量问题的有效方法是尽可能分配初始值。 变量在未初始化状态中越少越好。
技巧1:使用 let 和 const 来代替 var
技巧2:技巧2:增加内聚性
内聚描述模块的元素(命名空间、类、方法、代码块)内聚在一起的程度。凝聚力的测量通常被称为高凝聚力或低内聚。
(2). 访问不存在的属性
访问不存在的对象属性时,JS 返回undefined
。
let tuandui = {
title: 'bitt uit'
};
console.log(tuandui.alis) // => undefined
修改后:本身访问不存在的属性不会引发错误, 但尝试从不存在的属性值中获取数据时就会出现问题。 常见的的错误是 TypeError: Cannot read property <prop> of undefined
。
eg:
let tuandui = {
title: 'bitt uit'
};
tuandui.alis[0]
console.log(tuandui.alis[0]) // => Uncaught TypeError: Cannot read property '0' of undefined
JS 允许访问不存在的属性,这种允许访问的特性容易引起混淆:可能设置了属性,也可能没有设置属性,绕过这个问题的理想方法是限制对象始终定义它所持有的属性。
不幸的是,咱们常常无法控制对象
技巧3:检查属性是否存在
JS 提供了许多方法来确定对象是否具有特定属性:
-
obj.prop!== undefined
:直接与undefined
进行比较 -
typeof obj.prop!=='undefined'
:验证属性值类型 -
obj.hasOwnProperty('prop')
:验证对象是否具有自己的属性 -
'prop' in obj
:验证对象是否具有自己的属性或继承属性
建议使用 in
操作符。in
操作符的存在表明一个明确的意图,即检查对象是否具有特定的属性,而不访问实际的属性值。
obj.hasOwnProperty('prop')
也是一个很好的解决方案,它比 in
操作符稍长,仅在对象自己的属性中进行验证。
涉及与undefined进行比较剩下的两种方式可能有效,obj.prop!== undefined
和typeof obj.prop!=='undefined'
看起来冗长。
通过in的实例:
function addTo(array, toAppend) {
const arrayCopy = array.slice();
if ('first' in toAppend) {
arrayCopy.unshift(toAppend.first);
}
if ('last' in toAppend) {
arrayCopy.push(toAppend.last);
}
return arrayCopy;
}
addTo([1,2,3], {
last:4
});
console.log(addTo([1,2,3], {
last: 4
})) //[0, 1, 2, 3, 4]
addTo([10], {
first: 0,
last: false
});
console.log(addTo([10], {
first: 0,
last: false
})) //[0, 10, false]
技巧4:解构访问对象属性
在访问对象属性时,如果属性不存在,有时需要指示默认值。可以使用in
和三元运算符来实现这一点。
eg:
const thatobj = {};
const prop = 'prop' in thatobj ? thatobj.prop : 'default';
prop; // => 'default'
console.log(prop)
对象解构:对象解构允许将对象属性值直接提取到变量中,并在属性不存在时设置默认值,避免直接处理undefined
const thatobj = {};
const {
ab = 'defaults'
} = thatobj;
ab; //
console.log(ab) //=> 'defaults'
eg:
function quote(str, {
char = '"',
skipIfQuoted = true
} = {}) {
const length = str.length;
if (skipIfQuoted &&
str[0] === char &&
str[length - 1] === char) {
return str;
}
return char + str + char;
}
quote('Hello World', {
char: '*'
});
console.log(quote('Hello World', {
char: '*'
}))// => '*Hello World*'
quote('Goods day');
console.log(quote('Goods day'))// => "Goods day"
技巧5: 用默认属性填充对象
如果不需要像解构赋值那样为每个属性创建变量,那么丢失某些属性的对象可以用默认值填充。
ES6 Object.assign(target,source1,source2,...)
将所有可枚举的自有属性的值从一个或多个源对象复制到目标对象中,该函数返回目标对象。
例如,需要访问unsafes
对象的属性,该对象并不总是包含其完整的属性集。
为了避免从unsafes访问不存在的属性,让我们做一些调整:
定义包含默认属性值的defaults
对象
调用Object.assign({},defaults,unsafes)
来构建新的对象options
。 新对象从unsafes接收所有属性,但缺少的属性从defaults
对象获取。
eg:
const unsafe = {
fontSize:60
};
const defaults = {
fontSize: 100,
color: 'white'
};
const options = Object.assign({}, defaults, unsafe);
options.fontSize;
console.log(options.fontSize)// => 60
options.color;
console.log(options.color)// => 'white'
枚举源对象的顺序很重要:后面的源对象属性会覆盖前面的源对象属性。
还有一种简单的方法就是使用ES6中展开运算符:
const unsafeOptions = {
fontSize: 18
};
const defaults = {
fontSize: 16,
color: 'black'
};
const options = {
...defaults,
...unsafeOptions
};
options.fontSize; // => 18
options.color; // => 'black'
对象初始值设定项从defaults
和unsafeOptions
源对象扩展属性。 指定源对象的顺序很重要,后面的源对象属性会覆盖前面的源对象。
使用默认属性值填充不完整的对象是使代码安全且持久的有效策略。无论哪种情况,对象总是包含完整的属性集:并且无法生成undefined的属性。
(3). 函数参数
函数参数隐式默认为undefined
通常,用特定数量的参数定义的函数应该用相同数量的参数调用。在这种情况下,参数得到期望的值
function multip(a, b) {
a; // => 5
b; // => 3
return a * b;
}
multip(2, 3); // => 6
调用multip(5,3)
使参数a
和b
接收相应的5
和3
值,返回结果:5 * 3 = 15
。
在调用时省略参数会发生什么?
function multip(a, b) {
a; // => 2
b; // => undefined
return a * b;
}
multiply(2); // => NaN
函数multip(a, b){}
由两个参数a
和b
定义。调用multip(5)
用一个参数执行:结果一个参数是2
,但是b
参数是undefined
。
技巧6: 使用默认参数值
//技巧6: 使用默认参数值
//es5
function mult(a1, b1) {
if (b1 === undefined) {
b1 = 100;
}
return a1 * b1;
}
mult(5);
console.log(mult(10)) // => 50
function mult(a1, b1) {
if (b1 === undefined) {
b1 = 100;
}
return a1 * b1;
}
mult(5);
console.log(mult(10)) // => 50
//虽然所提供的分配默认值的方法有效,但不建议直接与undefined值进行比较。它很冗长
//这里可以使用 ES6 的默认值:
function mults(a, b = 100) {
return a * b;
}
mults(50);
console.log(mults(50))// => 5000
mults(50, undefined);
console.log(mults(50, undefined))// => 5000
(4).函数返回值
没有return
语句,JS 函数返回undefined
。
在JS中,没有任何return
语句的函数隐式返回undefined
:
function square(x) {
const res = x * x;
}
square(2);
console.log(square(2))// => undefined
square()
函数没有返回计算结果,函数调用时的结果undefined
。
当return
语句后面没有表达式时,默认返回 undefined
。
function squares(x) {
const res = x * x;
return;
}
squares(10);
console.log(squares(2))// => undefined
return;
语句被执行,但它不返回任何表达式,调用结果也是undefined
。
function square(x) {
const res = x * x;
return res;
}
square(2); // => 4
技巧7: 不要相信自动插入分号
JS 中的以下语句列表必须以分号(;)
结尾:
空语句
let
,const
,var
,import
,export
声明
表达语句
debugger
语句
continue
语句,break
语句
throw
语句
return
语句
如果使用上述声明之一,请尽量务必在结尾处指明分号
let
声明和 return
语句结束时,强制性写分号。
当然,不要在return
和返回的表达式之间放置换行符。
(5).void 操作符
void <expression>
计算表达式无论计算结果如何都返回undefined
。
void 1; // => undefined
void (false); // => undefined
void {name: 'John Smith'}; // => undefined
void Math.min(1, 3); // => undefined
void
操作符的一个用例是将表达式求值限制为undefined
,这依赖于求值的一些副作用。
(6).未定义数组
访问越界索引的数组元素时,会得到undefined
。
const colors = ['blue', 'white', 'red'];
colors[5]; // => undefined
colors[-1]; // => undefined
colors
数组有3个元素,因此有效索引为0
,1
和2
。
因为索引5
和-1
没有数组元素,所以访问colors[5]
和colors[-1]
值为undefined
。
JS 中,可能会遇到所谓的稀疏数组。这些数组是有间隙的数组,也就是说,在某些索引中,没有定义元素。
当在稀疏数组中访问间隙(也称为空槽)时,也会得到一个undefined
。
下面的示例生成稀疏数组并尝试访问它们的空槽
const sparse1 = new Array(3);
sparse1; // => [<empty slot>, <empty slot>, <empty slot>]
sparse1[0]; // => undefined
sparse1[1]; // => undefined
const sparse2 = ['white', ,'blue']
sparse2; // => ['white', <empty slot>, 'blue']
sparse2[1]; // => undefined
使用数组时,为了避免获取undefined
,请确保使用有效的数组索引并避免创建稀疏数组。
4).undefined和null之间的区别
undefined
和null
之间的主要区别是什么?这两个特殊值都表示为空状态。
主要区别在于undefined
表示尚未初始化的变量的值,null
表示故意不存在对象。
number 定义了但没有赋值。
let number;
number; // => undefined
number
变量未定义,这清楚地表明未初始化的变量。
当访问不存在的对象属性时,也会发生相同的未初始化概念
const obj = { firstName: 'Dmitri' };
obj.lastName; // => undefined
因为obj
中不存在lastName
属性,所以JS正确地将obj.lastName
计算为undefined
。
在其他情况下,你知道变量期望保存一个对象或一个函数来返回一个对象。但是由于某些原因,你不能实例化该对象。在这种情况下,null是丢失对象的有意义的指示器。
例如,clone()
是一个克隆普通JS对象的函数,函数将返回一个对象
function clone(obj) {
if (typeof obj === 'object' && obj !== null) {
return Object.assign({}, obj);
}
return null;
}
clone({name: 'John'}); // => {name: 'John'}
clone(15); // => null
clone(null); // => null
typeof操作符区分了这两个值
typeof undefined; // => 'undefined'
typeof null; // => 'object'
严格相等运算符===
可以正确区分undefined
和null
:
let nothing = undefined;
let missingObject = null;
nothing === missingObject; // => false