JavaScript 在开发过程中可能会出现很多奇怪的问题,以下是一些示例:
变量提升是 JavaScript 中一个常见的问题,特别是当没有充分理解变量作用域和声明提升时。以下是一个变量提升导致的问题示例:
var a = 1;
function foo() {
console.log(a);
var a = 2;
}
foo(); // 输出:undefined
预期输出是 1,但实际上输出的是 undefined。这是因为在函数内部声明了一个同名变量 a,函数作用域内的变量声明被提升到了函数开头,所以 console.log(a) 实际上输出的是 undefined。
解决该问题的方法是使用 let 或 const 关键字声明变量,这样可以避免变量提升和作用域污染:
let a = 1;
function foo() {
console.log(a);
let a = 2;
}
foo(); // 输出:报错 Uncaught ReferenceError: Cannot access 'a' before initialization
this 关键字在 JavaScript 中非常重要,但也很容易导致问题。this 关键字的指向是动态的,它的值取决于函数的调用方式。以下是一个 this 关键字导致的问题示例:
var name = "John";
var person = {
name: "Bob",
sayName: function () {
console.log("name", this.name);
},
};
var sayName = person.sayName;
sayName();
预期输出是 "Bob",但实际上输出的是 "John"。这是因为在全局作用域中调用 sayName 函数时,this 指向的是全局对象 window,而全局作用域中定义的 name 变量值为 "John"。
解决该问题的方法是使用 call、apply 或 bind 方法来改变 this 的指向:
sayName.call(person);
console.log(false == "0"); // 输出 true
console.log(false === "0"); // 输出 false
在第一行中,"0" 被转换为 false,因此 false == false,结果为 true。在第二行中,使用了严格相等运算符 ===,它不会自动转换类型,因此 false 和 "0" 不相等,结果为 false。
JavaScript 中的 == 和 === 都是比较运算符,用于比较两个值是否相等。它们之间的主要区别在于它们在比较时进行的类型转换的方式不同。
== 比较运算符会进行类型转换,它在比较之前会尝试将两个操作数转换为相同的类型。具体来说,如果比较的两个操作数的类型不同,则会按照一定的规则进行类型转换,转换后再进行比较。以下是 == 运算符的类型转换规则:
例如:
1 == "1"; // true
true == 1; // true
null == undefined; // true
=== 恒等运算符不会进行类型转换,它仅在两个操作数严格相等时返回 true。两个操作数严格相等的定义是它们的类型和值都相等。以下是 === 运算符的比较规则:
例如:
1 === "1"; // false
true === 1; // false
null === undefined; // false
因为 === 恒等运算符不会进行类型转换,所以它通常比 == 比较运算符更加严格和安全。在比较两个值时,建议优先使用 === 运算符。只有在明确需要进行类型转换时,才应该使用 == 运算符。
异步操作是 JavaScript 中一个重要的特性,但也容易导致一些问题。以下是一个异步操作导致的问题示例:
for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}
// 输出 5、5、5、5、5
预期输出是 0、1、2、3、4,但实际上输出的是 5、5、5、5、5。因为 setTimeout 函数是一个异步操作,它会在循环结束后再执行。当 setTimeout 函数被调用时,i 的值已经变成了 5,因此它会输出 5,而不是预期的 0、1、2、3 和 4。为了解决这个问题,可以使用立即调用的函数表达式(IIFE) 或 let 关键字来解决变量作用域的问题。
通过使用 IIFE 来来解决该问题:
for (var i = 0; i < 5; i++) {
(function (j) {
setTimeout(function () {
console.log(j);
}, 1000);
})(i);
}
// 输出 0、1、2、3、4
在 JavaScript 中,引用类型(如数组和对象)的比较可能导致一些奇怪的问题。以下是一个引用类型比较导致的问题示例:
console.log([] == []); // 输出 false
console.log([] === []); // 输出 false
这是因为 JavaScript 中比较引用类型时,比较的是它们在内存中的地址,而不是它们的内容。因此,两个空数组虽然看起来相同,但它们在内存中的地址不同,因此比较结果为 false。
不恰当的变量命名可能导致一些问题。以下是一个变量命名导致的问题示例:
var NaN = "not a number";
console.log(NaN); // 输出 NaN
console.log(typeof NaN); // 输出 "number"
因为 NaN 是 JavaScript 中一个关键字,表示 Not a Number,不应该被用作变量名。因为变量名和关键字相同,所以 typeof 操作符返回了 "number",而不是预期的 "string"。
JavaScript 中有很多不同的数据类型,类型转换可能导致一些奇怪的问题。以下是一个数据类型转换导致的问题示例:
console.log(1 + "2" + "2"); // 输出 "122"
console.log(1 + +"2" + "2"); // 输出 "32"
console.log(1 + -"1" + "2"); // 输出 "02"
console.log(+"1" + "1" + "2"); // 输出 "112"
console.log("A" - "B" + "2"); // 输出 "NaN2"
console.log("A" - "B" + 2); // 输出 NaN
这些奇怪的输出都是因为类型转换造成的,例如在第一行中,数字 1 和字符串 "2" 相加,得到字符串 "12",然后再和字符串 "2" 相加,得到字符串 "122"。
NaN 是一种特殊的数值,表示 "Not a Number"。在 JavaScript 中,NaN 与任何值都不相等,包括它自己。以下是一个 NaN 比较导致的问题示例:
console.log(NaN == NaN); // 输出 false
console.log(NaN === NaN); // 输出 false
解决该问题的方法是使用全局函数 isNaN() 来判断一个值是否为 NaN:
console.log(isNaN(NaN)); // 输出 true
在 JavaScript 中,使用浮点数进行计算时,可能会出现精度问题。例如,0.1 + 0.2 的结果并不是 0.3。以下是一个精度问题导致的问题示例:
console.log(0.1 + 0.2 == 0.3); // 输出 false
解决该问题的方法是将浮点数转换为整数进行计算,最后再将结果除以 10。或者使用 Number.EPSILON 来比较两个浮点数是否相等:
console.log(Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON); // 输出 true
参考:JavaScript 中 0.1+0.2 不等于 0.3 的问题
在 JavaScript 中,最大整数可以通过访问 Number.MAX_SAFE_INTEGER 属性来获取。这个属性的值为 9007199254740991,它是 JavaScript 中可安全表示的最大整数。超过这个值的整数将不再被准确表示。例如,9007199254740992 将被表示为 9007199254740992,但是 9007199254740993 将被表示为 9007199254740992,因为它超出了 JavaScript 可以准确表示的整数范围。
在 JavaScript 中,当对布尔值使用算术运算符时,它们会被自动转换为数字类型。true 被转换为数字 1,false 被转换为数字 0。
console.log(true + true); // 输出:2
console.log(true - true); // 输出:0
闭包中引用的外部变量不会被垃圾回收,可能导致内存泄漏。以下是导致内存泄漏的示例代码:
function outerFunction() {
var bigArray = new Array(1000000);
return function innerFunction() {
console.log(bigArray);
};
}
var inner = outerFunction();
// 忘记释放 inner 函数会导致内存泄漏
解决方法:
在使用闭包时,确保在不再需要它时释放它。在此示例中,可以将 inner 变量设置为 null 以释放闭包。
function outerFunction() {
var bigArray = new Array(1000000);
return function innerFunction() {
console.log(bigArray);
};
}
var inner = outerFunction();
// 使用完 inner 函数后释放它
inner = null;
如果多个闭包共享同一个外部变量,它们可能会意外地修改该变量的值,导致意想不到的结果。以下是示例代码:
function createFunctions() {
var result = [];
for (var i = 0; i < 5; i++) {
result[i] = function () {
console.log("Index: " + i);
};
}
return result;
}
var functions = createFunctions();
// 所有函数输出的值都是 5,而不是预期的 0、1、2、3、4
functions[0](); // 输出 "Index: 5"
functions[1](); // 输出 "Index: 5"
functions[2](); // 输出 "Index: 5"
functions[3](); // 输出 "Index: 5"
functions[4](); // 输出 "Index: 5"
解决方法:
在循环中使用闭包时,需要创建一个新的作用域来存储循环变量的值。可以使用立即调用的函数表达式(IIFE)来创建一个新的作用域。以下是修改后的代码:
function createFunctions() {
var result = [];
for (var i = 0; i < 5; i++) {
(function (i) {
result[i] = function () {
console.log("Index: " + i);
};
})(i);
}
return result;
}
var functions = createFunctions();
// 此时,每个函数都输出正确的值
functions[0](); // 输出 "Index: 0"
functions[1](); // 输出 "Index: 1"
functions[2](); // 输出 "Index: 2"
functions[3](); // 输出 "Index: 3"
functions[4](); // 输出 "Index: 4"
在循环中使用闭包时,可能会出现问题。如果在闭包中使用循环变量,它们将共享同一个值,可能导致错误结果。以下是示例代码:
for (var i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}
上述代码属于闭包情况。请参考《JavaScript 回调函数属于闭包?》
解决方法:
与上一个示例类似,可以使用 IIFE 创建一个新的作用域来存储循环变量的值。以下是修改后的代码:
for (var i = 1; i <= 5; i++) {
(function (i) {
setTimeout(function () {
console.log(i);
}, 1000);
})(i);
}
或者可以使用 let 关键字声明循环变量,它会在每次迭代中创建一个新的变量,从而避免共享变量的问题。以下是使用 let 关键字的代码:
for (let i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}
使用 let 关键字是更加简单和可读性更高的方法,因为它会自动解决共享变量的问题。但是在一些较老的浏览器版本中可能不支持 let 关键字,因此使用 IIFE 是更通用的解决方法。
作者:飞仔FeiZai
出处:https://www.cnblogs.com/yuzhihui/p/17174558.html
一直以来进行了比较多的微信小程序开发... 总会接触到一些和官方组件或 api 相关或其无法解决的需求,于是决定在这里小小的整理一下微信小程序开发的一些技巧
小程序提供onShareAppMessage 函数,此函数只支持分享给我微信朋友,小程序如何分享到朋友圈呢?使用canvas绘制一张图片,并用wx.previewImage预览图片,然后长按图片保存图片到手机。
前端新手程序员不知道的 20个小技巧:作为前端开发者,使用双显示器能大幅提高开发效率、学编程最好的语言不是PHP,是English、东西交付之前偷偷测试一遍、问别人之前最好先自己百度,google一下、把觉得不靠谱的需求放到最后做,很可能到时候需求就变了...
本地的 IP 地址是分配给你计算机上的内部硬件或虚拟网卡的本地/私有 IP 地址。根据你的 LAN 配置,上述 IP 地址可能是静态或动态的。公共的 IP 地址是你的 Internet 服务提供商(ISP)为你分配的公共/外部 IP 地址。
使用 :not() 在菜单上应用/取消应用边框;给body添加行高;所有一切都垂直居中;逗号分隔的列表;使用负的 nth-child 选择项目;对图标使用SVG;优化显示文本;对纯CSS滑块使用 max-height;继承 box-sizing
禁用右键点击;禁用搜索文本框;新窗口打开链接;检测浏览器;预加载图片;样式筛选;列高度相同;字体大小调整;返回页面顶部;获取鼠标的xy坐标;验证元素是否为空;替换元素
为你网站的用户留下良好的第一印象是非常必要的。随着商业领域的竞争,拥有一个吸引人的网站可以帮助你脱颖而出。研究表明,如果加载时间超过3秒,会有 40% 的用户放弃访问你的网站
清除浮动主要用于子元素浮动(float)之后,父元素无法撑起高度和宽度。文字少时居中,多时靠左因为div嵌套着p,所以p的尺寸并不会超过div。但是要注意,当p的内容为英文单词组成的时候
这次我们主要来分享11个在日常教程中不常被提及的JavaScript小技巧,他们往往在我们的日常工作中经常出现,但是我们又很容易忽略。Set类型是在ES6中新增的,它类似于数组,但是成员的值都是唯一的
为什么要在JavaScript里写CSS?避免命名全局污染,条件和动态样式(比如选择主题色之类的),在框架层面进行限制或补充(比如补全供应商前缀),避免业务人员使用奇技淫巧
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!