JS中this的指向问题
1. 在全局环境下
在全局环境中(在浏览器中是 window
对象,在Node.js中是 global
对象),this
指向全局对象。
console.log(this === window); // 在浏览器中为 true
console.log(this.document !== undefined); // 在浏览器中为 true
2. 作为对象的方法
当函数作为对象的方法被调用时,this
指向该对象。
var obj = {
x: 42,
getX: function() {
return this.x;
}
};
console.log(obj.getX()); // 输出 42,此时 this 指向 obj
3. 函数作为构造函数
当函数使用 new
关键字被调用时,该函数作为构造函数,this
被绑定到新创建的对象上。
function Person(name) {
this.name = name;
}
var person = new Person("Alice");
console.log(person.name); // 输出 Alice
//this指向person
4. 箭头函数
箭头函数不绑定自己的 this
,它会继承外围作用域的this
var obj = {
x: 42,
getX: function() {
return () => this.x;
}
};
console.log(obj.getX()()); // 输出 42,因为箭头函数捕获了 getX 方法中的 this(即 obj)
5. 显式设置 this
(call
, apply
, bind
)
Function.prototype.call()
, Function.prototype.apply()
, 和 Function.prototype.bind()
方法允许你显式设置 this
的值。
call
方法接受一个this
值和一个参数列表。apply
方法接受一个this
值和一个包含多个参数的数组。bind
方法返回一个新函数,在bind
被调用时,这个新函数的this
被指定为bind
的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
function greet() {
return "Hello, " + this.name;
}
var obj = { name: "Alice" };
console.log(greet.call(obj)); // 输出 "Hello, Alice"
console.log(greet.apply(obj)); // 输出 "Hello, Alice"
var boundGreet = greet.bind(obj);
console.log(boundGreet()); // 输出 "Hello, Alice"
JS的执行机制
1. 调用栈(Call Stack)
调用栈是 JavaScript 代码执行时用来存储函数调用信息的数据结构。每当一个函数被调用时,它的信息(包括参数和局部变量)就会被推入调用栈的顶部。当函数执行完成后,它的信息就会被从调用栈中弹出,控制权就回到了前一个函数(即调用者)。
如果调用栈中的函数太多,导致调用栈过大,就会引发“栈溢出”(Stack Overflow)错误。
2. 事件循环(Event Loop)
JavaScript 是单线程的,这意味着它一次只能执行一个任务。然而,JavaScript 需要能够处理异步操作(如网络请求、文件读取等),这些操作可能会花费很长时间才能完成。为了实现这一点,JavaScript 使用了事件循环。
事件循环允许 JavaScript 执行代码块,将耗时任务(如 I/O 操作)推送到“任务队列”(Task Queue)中,然后继续执行其他代码。一旦调用栈为空(即当前没有正在执行的代码),事件循环就会从任务队列中取出任务,并将其推送到调用栈中执行。
3. 宏任务(MacroTask)与微任务(MicroTask)
任务队列实际上可以进一步细分为宏任务队列和微任务队列。
- 宏任务:包括整体代码执行、
setTimeout
、setInterval
、setImmediate
(Node.js 独有)、I/O 操作、UI 渲染等。 - 微任务:包括
Promise.then
、MutationObserver
(DOM 变更观察者)、process.nextTick
(Node.js 独有)等。
在执行过程中,每次从宏任务队列中取出一个任务执行完毕后,会检查并连续执行完所有的微任务队列中的任务,然后再从宏任务队列中取出下一个任务执行。这种机制确保了微任务总是优先于宏任务执行。
4. 异步与同步
JavaScript 的异步操作是通过回调函数、Promises、async/await 等机制实现的。这些机制允许代码在等待某个异步操作完成时继续执行其他任务,从而提高了应用程序的响应性和性能。
元素偏移量offset
offset系列属性 | 作用 |
offsetWidth 和 offsetHeight | 返回元素自身,包括元素的内容(content)宽度/高度、内边距(padding)和边框(border),但不包括外边距(margin)和滚动条(如果有的话),返回数值不带单位 |
offsetLeft 和 offsetTop | 返回元素相对于其带有定位(position为非static)的父元素(offsetParent)的左边距和上边距的偏移量,如果元素的所有父元素都没有定位,则相对于<body> 元素计算偏移量,返回数值不带单位 |
offsetParent | 返回作为该元素带有定位的父级元素,如果父级没有定位,则offsetParent为<body> 或<html> 元素,返回对象,最近定位的父元素 |
client系列属性的作用
client系列属性 | 作用 |
element.clientTop | 返回元素上边框的大小,值表示顶部边框的宽度,但不包括上内边距(padding-top)或滚动条,返回数值不带单位 |
element.clientLeft | 返回元素左边框的大小,表示左边框的宽度,但不包括左内边距(padding-left)或滚动条(返回数值不带单位 |
element.clientWidth | 返回元素的内部宽度,包括内边距(padding),但不包括边框(border)、外边距(margin)或滚动条(如果存在且未计算在内),返回数值不带单位 |
element.clientHeight | 返回元素的内部宽度,包括内边距(padding),但不包括边框(border)、外边距(margin)或滚动条(如果存在且未计算在内),返回数值不带单位 |
scroll系列属性的作用
scroll系列属性 | 作用 |
element.scrollTop | 返回元素在垂直方向上已滚动的像素值。这个值表示元素的上边缘与其可视区域上边缘之间的距离。如果元素未滚动,这个值通常是0,可设置 |
element.scrollLeft | 于水平方向上的滚动。返回元素在水平方向上已滚动的像素值。这个值表示元素的左边缘与其可视区域左边缘之间的距离,可设置 |
element.scrollWidth | 返回元素的滚动宽度,即包括因滚动而隐藏在浏览器可视区外的内容在内的元素的总宽度。这个值等于元素内容的实际宽度加上内边距(padding),但不包括边框(border)、外边距(margin)或滚动条本身(如果滚动条不占用空间的话),scrollWidth是一个只读属性,不能通过设置来改变其值 |
element.scrollHeight | 用于垂直方向上的滚动。返回元素的滚动高度,即包括因滚动而隐藏在浏览器可视区外的内容在内的元素的总高度。这个值等于元素内容的实际高度加上内边距(padding),但不包括边框(border)、外边距(margin)或水平滚动条(如果存在且未计算在内),只读属性,不能通过设置来改变其值 |
代码示例:
<body>
<style>
.parent{
border: 2px solid rgb(182, 115, 115);
background-color: #5bca3a;
width: 400px;
height: 400px;
position: relative;
padding: 10px;
left: 5px;
top: 5px;
}
.parent .child{
position: absolute;
border: #1287db solid 2px;
width: 100px;
height: 100px;
background-color: #a2d1f3;
}
</style>
<div class="parent">
<div class="child"></div>
</div>
<script>
var child=document.querySelector('.child');
console.log(child.offsetLeft,child.offsetTop); //返回元素在其父节点中的偏移位置
// console.log(child.offsetParent.offsetLeft,child.offsetParent.offsetTop); //返回元素的第一个定位先的偏移位置
console.log(child.offsetParent);
console.log(child.offsetWidth,child.offsetHeight); //返回元素的宽度和高度
console.log(child.clientWidth,child.clientHeight); //返回元素的可见宽度和高度
console.log(child.scrollWidth,child.scrollHeight); //返回元素的滚动宽度和高度
</script>
</body>
offset和style的区别
1.获取样式来源
- style:主要获取元素的内联样式表中的样式值
- offset:可以得到任意样式表中的样式值,包括内联样式、嵌入样式和外部样式表中定义的样式。
2.返回值类型不同
- offset:获取的数值通常没有单位。
- style:获取带有单位的字符串。
3.属性只包含范围
- offset:其属性(如
offsetWidth
、offsetHeight
)包含元素的边框(border)、内边距(padding)和内容区域(width/height)的宽度和高度。但通常不包括外边距(margin)和滚动条(除非滚动条占据了内容区域的空间)。 - style:
style.width
和style.height
通常只获取或设置内容区域的宽度和高度,不包括边框和内边距
4.可读写性
- offset:offset系列的属性(如
offsetWidth
、offsetHeight
、offsetLeft
、offsetTop
等)主要是只读的,用于获取元素的信息,而不能直接通过赋值来改变元素的大小或位置。 - style:
style
属性下的样式值(如style.width
、style.height
等)通常是可读写的,即可以通过赋值来改变元素的样式。