基本数据类型(存放在栈中)
基本数据类型是指存放在栈中的简单数据段,数据大小确定,内存空间大小可以分配,它们是直接按值存放的,所以可以直接按值访问
引用数据类型(存放在堆内存中的对象,每个空间大小不一样,要根据情况进行特定的配置)
引用类型是存放在堆内存中的对象,变量其实是保存的在栈内存中的一个指针(保存的是堆内存中的引用地址),这个指针指向堆内存。
引用类型数据在栈内存中保存的实际上是对象在堆内存中的引用地址。通过这个引用地址可以快速查找到保存中堆内存中的对象
1、声明变量时内存分配不同
原始类型:在栈中,因为占据空间是固定的,可以将他们存在较小的内存中-栈中,这样便于迅速查询变量的值
引用类型:存在堆中,栈中存储的变量,只是用来查找堆中的引用地址。
这是因为:引用值的大小会改变,所以不能把它放在栈中,否则会降低变量查寻的速度。相反,放在变量的栈空间中的值是该对象存储在堆中的地址。地址的大小是固定的,所以把它存储在栈中对变量性能无任何负面影响
2、不同的内存分配带来不同的访问机制
在JavaScript中是不允许直接访问保存在堆内存中的对象的,所以在访问一个对象时,首先得到的是这个对象在堆内存中的地址,然后再按照这个地址去获得这个对象中的值,这就是传说中的按引用访问。
而原始类型的值则是可以直接访问到的。
3、复制变量时的不同
1)原始值:在将一个保存着原始值的变量复制给另一个变量时,会将原始值的副本赋值给新变量,此后这两个变量是完全独立的,他们只是拥有相同的value而已。
2)引用值:在将一个保存着对象内存地址的变量复制给另一个变量时,会把这个内存地址赋值给新变量,
也就是说这两个变量都指向了堆内存中的同一个对象,他们中任何一个作出的改变都会反映在另一个身上。(复制对象时并不会在堆内存中新生成一个一模一样的对象,只是多了一个保存指向这个对象指针的变量)
4、参数传递的不同(把实参复制给形参的过程)
首先我们应该明确一点:ECMAScript中所有函数的参数都是按值来传递的。
但是为什么涉及到原始类型与引用类型的值时仍然有区别呢?还不就是因为内存分配时的差别。
1)原始值:只是把变量里的值传递给参数,之后参数和这个变量互不影响。
2)引用值:对象变量它里面的值是这个对象在堆内存中的内存地址.
因此它传递的值也就是这个内存地址,这也就是为什么函数内部对这个参数的修改会体现在外部的原因了,因为它们都指向同一个对象。
3. 数据类型判断
- typeof
typeof 基本可以正常判断基本数据类型,没法正确识别 Null,可以区分function,但是对于其他引用数据类型会返回 Object,这是因为 typeof 的原理:
-
所有数据类型在计算机中存储的都是按照“二进制”存储
-
Null -> 000000
-
对象都以 000 开头
-
typeof 检测的时候,是按照计算机存储的二进制来检测的
typeof null // “object”
typeof function(){} // “function”
typeof [] // “object”
typeof /./ // “object”
- instanceof
原理:根据原型对象来判断类型
function instanceof(left, right) {
const rightVal = right.prototype
const leftVal = left.proto
// 若找不到就到一直循环到父类型或祖类型
while(true) {
if (leftVal === null) {
return false
}
if (leftVal === rightVal) {
return true
}
leftVal = leftVal.proto // 获取祖类型的__proto__
}
}
首先需要了解JS的作用域链,内部可以访问外部的变量,但是外部却不可以访问内部的变量,所以当我们有需求访问函数内部的变量时,我们就需要利用闭包去访问。
因此可以明确闭包的四个特性:
-
闭包一定是函数对象
-
闭包和词法作用域,作用域链,垃圾回收机制息息相关
-
当函数一定是在其定义的作用域外进行的访问时,才产生闭包
-
闭包是由该函数和其上层执行上下文共同构成
我们可以通过最简单的闭包例子来分析
function foo() {
let a = 2;
function bar() {
console.log( a );
}
return bar;
}
console.log(a) // Uncaught ReferenceError: a is not defined
let baz = foo();
baz(); // 2
但是滥用闭包同样会造成弊端
- 内存泄漏(Memory Leak)
闭包阻止了垃圾回收机制对变量进行回收,因此变量会永远存在内存中,即使当变量不再被使用时,这样会造成内存泄漏,会严重影响页面的性能。因此当变量对象不再适用时,我们要将其主动释放,尤其是涉及 DOM 的操作时。
function foo() {
let a = 2;
function bar() {
console.log( a );
}
return bar;
}
let baz = foo();
baz(); //baz指向的对象会永远存在堆内存中
baz = null; //如果baz不再使用,将其指向的对象释放
- this指向不明
var object = {
name: ‘object’,
getName: function() {
return function() {
console.log(this.name)
}
}
}
object.getName()() // underfined
事件流分为3个阶段:
1 - 5 为捕获阶段
5 - 6 为目标阶段
6 - 10 为冒泡阶段
优点:
-
节省内存
-
新增对象无需再次绑定
实现方法: 通过给父元素增加监听器获取target,便可拿到子元素的标签名和class,id等属性,从而进行操作
例:
window.onload = function(){
var oUl = document.getElementById(‘ul1’)
oUl.onmouseover = function(ev){
var ev = ev || window.event
var oLi = ev.srcElemnt || ev.target
oLi.style.background = ‘red’
最后
给大家分享一些关于HTML的面试题。
= function(){
var oUl = document.getElementById(‘ul1’)
oUl.onmouseover = function(ev){
var ev = ev || window.event
var oLi = ev.srcElemnt || ev.target
oLi.style.background = ‘red’
最后
给大家分享一些关于HTML的面试题。
[外链图片转存中…(img-lPmCNWaD-1718162523675)]
[外链图片转存中…(img-1QtJnrZB-1718162523677)]