八种类型:undefined、null、boolean、number、string、object、symbol(ES6)、bigint(ES6)
栈:基本数据类型(undefined、null、boolean、number、string)开辟新内存
堆:引用类型数组(对象、数组、函数)浅拷贝,内存地址不变
两者存储位置不同:
基本数据类型:存储在栈中,占据空间小,大小固定,属于被频繁使用的数据,所以放入栈中存储
引用数据类型:存储在堆中,占据空间小,大小不固定,在栈中存储了指针,指向堆中该实体的起始地址,当寻找引用值时,回首先检索其在栈中的地址,然后从堆中获得实体
使用typeof,返回该数据类型
console.log(typeof 2)
使用instanceof,返回true或false
console.log(2 instanceof Number)
constructor,返回true或false;可以通过以下方法判断数据类型,也可以对象实例通过constrcutor对象访问他的构造函数
console.log((2).constructor === Number)
Object.prototype.toString.call()
Object.prototype.toString.call()使用Object对象的原型方法toString来判断数据类型
var a = Object.prototype.toString;
console.log(a.call(2))
Object.prototype.toString.call()
原型链
obj._proto_ === Array.prototype
ES6的Array.isArray()判断
Array.isArray(obj)
instanceof判断
obj instanceof Array
Array.prototype.isPrototypeOf()
Array.prototype.isPrototypeOf(obj)
都是基本数据类型,都只有一个值,即ubdefined和null;前者代表未定义,后者代表空对象;变量声明了但没有定义返回undefined,null赋值给一些可能会返回对象的变量,作为初始化。
intanceof运算符用于判断构造函数的prototype属性是否会出现在对象的原型链中的任何位置
function myInstanceof(left, right) {
// 获取对象原型
let proto = Object.getPrototypeOf(left)
// 获取构造函数的prototype对象
let prototype = right.prototype
// 判断构造函数的prototype对象是否在对象的原型链上
while (true) {
if (!proto) return false;
if (proto === prototype) return true
// 如果没有找到,就继续从其原型链上找,Object.getPrototypeOf方法来获取指定对象的原型
proto = Object.getPrototypeOf(proto)
}
}
let a = 0.1,b = 0.2
console.log(a+b); //0.30000000000000004
console.log((a+b).toFixed(2)); //0.30四舍五入两位
出现原因:计算机存储数据时,使用二进制,这两个数转换为二进制都是无限循环的数,相加就会出现误差
如何解决:一种就是上边的四舍五入;另一种就是利用ES6中的Number.EPSILON(2的负52次方),如果两数之和小于Number.EPSILON,就判断0.1+0.2 === 0.3
function numberepsilon(a,b) {
return Math.abs(a - b) < Number.EPSILON
}
console.log(numberepsilon(0.1+0.2,0.3)); //true
isNaN:接收参数后,会尝试将参数转换为数值,不能被转换为数值的值都会返回true,非数字传入也会返回true
Number.isNaN:先判断是不是数字,是数字再进行判断是否NaN,不会进行数据类型的转换,此方法判断更为准确
ToPrimitive方法:用来将值(基本类型、对象)转换为基本类型值。值为基本类型返回其本身
基本类型的隐式转换
let outobj = {
inobj:{a:1,b:2}
}
// 扩展运算符
let newobj = {...outobj}
newobj.inobj.a = 2
console.log(outobj); //{ inobj: { a: 2, b: 2 } }
// Object.assign()
let newobj2 = Object.assign({},outobj)
newobj2.inobj.a = 2
console.log(outobj); //{ inobj: { a: 2, b: 2 } }
相同点:两者都是浅拷贝
不同点:
扩展运算符:数组或对象中的每一个值都会被拷贝到一个新的数组或对象中,会复制ES6的symbol属性
Object.assign():接受第一个参数为目标对象,后面的所有参数作为源对象,然后把所有的源对象合并到目标对象中,会触发ES6 setter
typeof null的结果是Object
typeof NaN的结果是number
JS的包装类型:基本类型没有属性和方法,为了便于操作,在调用时JS会在后台隐式的将基本类型转换为对象进行操作
BigInt出现原因:为了解决超出最大精度后计算不准确的问题
判断是否为空对象
JSON自带的.stringify
console.log(JOSN.stringify(obj) == '{}')
ES6新增的Object.keys()
console.log(object.keys(obj).length < 0)
总结:创建一个新对象,设置对象的原型链,将构造函数的作用域赋给新对象,返回新对象
function a() {
let newObj = null;
let constructor = Array.prototype.shift.call(arguments)
let result = null
// 判断参数是否是一个函数
if(typeof constructor !== 'function'){
console.error('type error');
return
}
// 新建一个空对象,对象的原型为构造函数的prototype对象
newObj = Object.create(constructor.prototype)
// 将this指向新建对象,并执行函数
result = constructor.apply(newObj,arguments)
// 判断返回对象
let flag = result && (typeof result ==='object' || typeof result === 'function')
// 判断返回结果
return flag ? result : newObj
}
// 使用方法
a(构造函数,初始化函数)
//具体实现
function Person(name, age) {
this.name = name;
this.age = age;
}
// 使用 new 操作符创建一个新的 Person 实例
const person1 = new Person('Alice', 25);
console.log(person1.name); // 输出: Alice
console.log(person1.age); // 输出: 25
类数组对象:一个拥有length属性和若干索引属性的对象就可以被称为类数组对象,类数组对象和数组相似,但是不能调用数组的方法。常见的类数组对象有arguments和DOM方法的返回结果,一个函数也可以被看做,因为它含有length属性值,代表可以接收的参数个数
//类数组转换为数组的方法
Array.prototype.slice.call(arrayLike) //通过call调用数组的slice方法
Array.prototype.slice.call(arrayLike,0)
Array.prototype.slice.call([],arrayLike) //通过apply调用数组的concat方法
Array.from(arrayLike) //通过Array.from实现
数组的原生方法:
数组转换字符串:toString()、toLocaleString()、join(*),最后一个可以制定转换为字符串时的分隔符
尾部操作:弹出尾部元素pop()、尾部添加元素push(a)
首部操作:删除首部shift()、首部插入unshift(a)
重排序:反转数组reverse()、默认从小到大sort()
let a = [1, 2, 3, 4, 5, 0.1]
a.sort(function (a, b){ return b - a }) //[ 5, 4, 3, 2, 1, 0.1 ]
a.sort(function (a, b){ return a - b }) //[ 0.1, 1, 2, 3, 4, 5 ]
数组连接:concat()
let a = [1, 2, 3, 4, 5]
let b = [6, 7, 8, 9, 10]
let c = a.concat(b)
console.log(c); // [1, 2, 3, 4, 5 , 6, 7, 8, 9, 10]
数组截取:slice()
let a = [1, 2, 3, 4, 5]
console.log(a.slice(2)); // 倒数三个 [ 3, 4, 5 ]
console.log(a.slice(2,4)); // 左开右闭 [ 3, 4 ]
console.log(a.slice(1,5)); // 超出范围 [ 2, 3, 4, 5 ]
console.log(a.slice(-2)); // 倒数两个 [ 4, 5 ]
console.log(a.slice(2,-1)); // 取两个,从倒数第二个位置开始 [ 4, 5 ]
console.log(a.slice()); // 全部读取
数组插入&&删除&&替换:splice(startIndex, deleteCount, item1, item2, …)
const array = [1, 2, 3, 4, 5];
array.splice(2, 1); // 从索引 2 开始删除 1 个元素
console.log(array); // 输出: [1, 2, 4, 5]
array.splice(2, 0, 6); // 在索引 2 处插入元素 6,不删除任何元素
console.log(array); // 输出: [1, 2, 6, 3, 4, 5]
array.splice(2, 1, 6); // 从索引 2 处删除 1 个元素,并插入元素 6
console.log(array); // 输出: [1, 2, 6, 4, 5]
array.splice(1, 2, 6, 7, 8); // 从索引 1 处删除 2 个元素,并插入元素 6, 7, 8
console.log(array); // 输出: [1, 6, 7, 8, 4, 5]
array.splice(1, 3); // 从索引 1 处删除 3 个元素
console.log(array); // 输出: [1, 5]
array.splice(array.length, 0, 4); // 在数组末尾插入元素 4
console.log(array); // 输出: [1, 2, 3, 4]
// indexOf()方法
console.log(arr.indexOf(1)); //数组中有该元素,返回0,否则返回-1
// every()方法
console.log(arr.every(e => e > 0)); //true 数组中所有元素满足条件返回true,否则返回false
// some()方法
//用于检查数组中是否至少有一个元素满足指定的条件。这个方法会遍历数组的每个元素,直到找到一个满足条件的元素,如果找到了就返回 true,否则返回 false
console.log(arr.some(e => e % 2 == 0)); // true
// filter()方法
// 用于创建一个新数组,其中包含满足指定条件的原数组元素
console.log(arr.filter(e => e > 3)); //[ 4, 5 ]
// map()方法
// 用于创建一个新数组,该数组的元素是原始数组经过某个函数处理后的结果
console.log(arr.map(e => e * 2)); // [ 2, 4, 6, 8, 10 ]
// forEach()方法
// 用于遍历数组的每个元素,并对每个元素执行指定的操作
arr.forEach(function (e, index){
console.log(e);
}); //1 2 3 4 5
数组归并:reduce()对数组中的所有元素执行一个累加操作,并返回最终的累积结果
const numbers = [1, 2, 3, 4, 5];
// accumulator:累积的结果,它是每次回调执行后的返回值,也可以在回调函数中被更新;currentValue:当前正在处理的数组元素。
const sum = numbers.reduce(function(accumulator, currentValue) {
return accumulator + currentValue;
}, 0); // 初始值为 0
console.log(sum); // 输出 15,因为 1 + 2 + 3 + 4 + 5 = 15
arguments是一个对象,是从0开始依次递增的数字,有callee和length属性,与数组相似
遍历类数组方法:
// 将数组的方法用到类数组上
function foo(){
Array.prototype.forEach.call(arguments,a => console.log(a))
}
// 使用Array.from
function foo(){
const arrArgs = Array.from(arguments)
arrArgs.forEach(a => console.log(a))
}
// 使用展开运算符将类数组转化为数组
function foo(){
cosnt arrArgs = [...arguments]
arrArgs.forEach(a => console.log(a))
}
DOM(文档对象模型)和BOM(浏览器对象模型)用于与网页文档和浏览器进行交互
DOM:
DOM 是一种用于表示和操作网页文档结构的模型。
它将网页文档表示为一个树状结构,其中每个元素(如 HTML 标签)都是树的节点,可以通过 JavaScript 访问和操作。
DOM 提供了一组API,允许开发者通过JavaScript来读取、修改、添加和删除网页中的元素和内容。
DOM 的核心对象是document
,它代表整个网页文档,包含了HTML、CSS和JavaScript等内容的访问接口。
// 可以使用以下代码获取一个元素的引用并修改其内容
var element = document.getElementById("myElement");
element.innerHTML = "新内容";
BOM:
window
,既是通过JS方位浏览器窗口的一个接口,又是一个Global(全局)对象。// 可以使用以下代码在浏览器中打开一个新窗口
window.open("https://www.example.com", "_blank");
总结:
是通过JS的异步通信,从服务器获取数据再更新当前页面的对应部分,而不需要刷新真个网页
创建AJAX请求步骤:
var xhr = new XMLHttpRequest();
xhr.open(method, url, async);
xhr.setRequestHeader(header, value);
xhr.onload = function() {
if (xhr.status === 200) {
// 请求成功,处理响应数据
} else {
// 请求失败,处理错误
}
};
xhr.onerror = function() {
// 处理请求错误
};
会将变量核函数的生命提升到他们所在作用域的顶部,然后再执行代码
进行变量提升的步骤:
var
、let
、const
声明变量)以及函数声明(使用 function
关键字声明函数)好处:
问题:
var
声明变量时,因为它们在整个函数作用域内都可见,可能导致变量被意外覆盖不同点:
相同点:
节点的获取
document.getElementById(id) // 通过元素的 id 属性选取一个元素。
document.getElementsByClassName(className) // 通过元素的类名选取一个或多个元素。
document.getElementsByTagName(tagName) // 通过元素的标签名选取一个或多个元素。
document.querySelector(selector) // 通过 CSS 选择器选取匹配的第一个元素。
document.querySelectorAll(selector) // 通过 CSS 选择器选取所有匹配的元素。
节点的创建
document.createElement(tagName) // 创建一个新的元素节点。
document.createTextNode(text) // 创建一个包含文本内容的文本节点。
document.createDocumentFragment() // 创建一个文档片段,用于高效地批量添加和操作元素。
元素插入和删除
element.appendChild(childElement) // 将一个子元素添加到父元素的末尾。
element.insertBefore(newElement, referenceElement) // 在参考元素之前插入一个新元素。
element.removeChild(childElement) // 从父元素中移除一个子元素。
element.replaceChild(newElement, oldElement) // 替换一个子元素。
element.cloneNode(deep) // 克隆一个元素节点。
for
循环可以遍历数组的每个元素forEach
方法是数组的内置方法,用于遍历数组的每个元素并执行指定的回调函数map
方法也是数组的内置方法,它用于遍历数组的每个元素,并返回一个新的数组,新数组的每个元素都是原数组经过回调函数处理后的结果for...of
循环是一种现代 JavaScript 的遍历数组的方法,它可以遍历数组的值而不需要索引for...in
循环用于遍历对象的属性,但也可以用于遍历数组的索引。不过需要小心使用,因为它可能会遍历到数组的原型属性reduce
方法用于遍历数组的每个元素,并将它们累积到一个单一的值中filter
方法用于遍历数组的每个元素,并返回一个包含符合条件的元素的新数组var arr = [1, 2, 3, 4, 5];
// for循环
for (var i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
// forEach
arr.forEach(function (item) {
console.log(item);
});
// map
var newArr = arr.map(function (item) {
console.log(item * 2);
});
// for...of
for (var item of arr) {
console.log(item);
}
// for...in
for (var index in arr) {
console.log(arr[index]);
}
// reduce
var sum = arr.reduce(function (acc, item) {
return acc + item;
}, 0)
console.log(sum);
// filter
var evenNumbers = arr.filter(function(item) {
return item % 2 === 0;
});
console.log(evenNumbers);
JS内置对象主要指的是在程序执行前存在全局作用域里的由JS定义的一些全局值属性、函数和用来实例化其他对象的构造函数。一般常用的全局变量有NaN、underfined,全局函数有parseint()、parseFloat()等
JSON是一种基于文本的轻量级的数据交换格式,可以被任何的编程语言读取和作为数据格式来传递。在JS中提供了两个函数来实现。JSON.stringify(),将符合JSON格式的数据转换为JSON字符串;JSON.paras()将JSON类型的字符串转换为JS数据结构
JS脚本延迟加载方法
Unicode、UTF-8、UTF-16、UTF-32的区别:前者是编码字符集(字符集)、后三者是字符集编码(编码规则)
原码、反码、补码:原码就是一个数的二进制数;反码:正数的反码与原码相同,负数的反码为除去符号位,按位取反;补码:正数的相同,负数的为原码除符号位外所有为取反,然后加1
尾调用:发生在函数的最后一条语句,调用结果直接返回给函数的调用者,而不需要进行额外的计算或操作,有助于优化递归算法,减少内存消耗。
function factorial(n, accumulator = 1) {
if (n === 0) {
return accumulator;
}
return factorial(n - 1, n * accumulator); // 尾调用
}
console.log(factorial(5)); // 输出 120
强类型语言(Java、C++)与弱类型语言(JS)的区别:前者严谨,可以高效的避免一些错误;后者编译速度快
解释性语言(JS、Python)和编译性语言(C++)的区别:前者编译后即可在该平台运行,后者是在编译期间才编译;前者速度快,后者跨平台性好
ajax、axios、fetch:
addEventListener方法
// element:要添加事件监听器的 HTML 元素。
// eventType:要监听的事件类型,通常是一个字符串,例如 "click", "keydown", "mousemove" 等。
// callbackFunction:事件触发时要执行的回调函数。
// useCapture(可选参数):一个布尔值,用于指定事件是在捕获阶段(true)还是冒泡阶段(false)处理。大多数情况下,可以忽略此参数,使用默认值 false。
element.addEventListener(eventType, callbackFunction[, useCapture]);
// 案例
// 获取按钮元素
var button = document.getElementById("myButton");
// 添加点击事件监听器
button.addEventListener("click", function() {
alert("按钮被点击了!");
});