前端学习总结——JavaScript基础语法
变量类型和计算
- 值类型和引用类型
- 值在栈中存储,i = 1,将 i 的值赋值给 j ,在栈中新开辟一块区域存储 j = 1,j 跟 i 是两个独立的值,各自占用一块内存空间;
- 引用类型在堆中存储,a = {x: 20},a在堆中申请一个内存地址1,把值放在这个地址上,变量a在栈中保存,其值为内存地址1,这个地址指向{x: 20}这个对象,当把a赋值给b,实际上是把地址1赋值给b,通过b修改这个对象,a也会改变,也就是“传址”;
- 值类型和引用类型的保存实际上是计算机一种性能优化的手段,几乎所有语言都是这样保存数据的,由于值类型比引用类型占用的内存小,赋一次值开辟一块内存来存储一个值类型的数据也还无妨。
- 写个深拷贝的实现:
function deepClone (obj = {}) { if (typeof obj !== 'object' || obj == null){ // 不是对象或者数组, 直接返回 return obj; } let result; if (obj instanceof Array) { result = []; } else { result = {}; } for (let key in obj) { if (obj.hasOwnProperty(key)) { // 保证 key 不是原型的属性 //递归 result[key] = deepClone(obj[key]); } } return result; }
- 类型判断
- typeof能判断哪些类型?
- 数据类型分简单数据类型/复杂数据类型(原始类型/对象类型, 值类型/引用类型 ),简单数据类型有: undefined\number\string\Boolean\symbol,复杂数据类型:object\function\null\array\RegExp;
- typeof 能够检测所有值类型, 识别函数, 判断是否是引用类型(object: array, obj, null), 不可再细分, 更具体的 array 等引用类型需要用到 instanceof 来进行检测;
- 函数 function 是特殊的引用类型, 但不用于存储数据, 所以没有拷贝\复制函数一说。
- typeof能判断哪些类型?
原型和原型链
- 原型:创建一个构造函数函数,它会按照规则创建一个prototype属性,这个属性指向原型对象,这个原型对象上有一个默认constructor属性指回这个构造函数。原型上的非手动添加的属性和方法都继承自上一级构造函数的原型,调用这个构造函数创建新的实例,这个实例上的__proto__指针就被赋值为构造函数的原型对象。
实例与构造函数的原型之间有直接的联系,但是实例与构造函数之间没有。
- 原型链:原型是有层级的,实例通过原型继承属性和方法。每个构造函数都有一个原型,原型上有一个属性指回构造函数,而实例上有一个内部指针__proto__指向原型。原型可能是另一个构造函数的实例,那么他也有一个指针__proto__指向它的构造函数的原型,这种关系可以是很多层的,这就在实例和原型之间形成一条原型链。
原型链的顶端是object.prototype,其上定义的toString()、valueOf()、hasOwnProperty()。
实例的隐式原型引用的是对应构造函数的显式原型:children.__proto__ === Parent.prototype
- instanceof操作符:instanceof 返回一个布尔值,方法只要原型链上沾边就是true, [] instancemof Array/Object 返回true;
- 手写jQuery考虑插件和扩展性代码
class jQuery { constructor(selector) { const result = document.querySelectorAll(selector); const length = result.length; for (let i = 0; i < length; i++) { this[i] = result[i]; } this.length = length; this.selector = selector; } get(index) { return this[index];; } each(fn) { for (let i = 0; i < this.length; i++) { const elem = this[i]; fn(elem) } } on(type, fn) { return this.each(elem => { elem.addEventListener(type, fn, false) }) } } // 自己写的jQuery插件 jQuery.prototype.dialog = function (info) { alert(info); } // 造轮子 class myJQuery extends jQuery { constructor(selector) { super(selector); } // 扩展自己的方法 addClass(className) { } style(data) { } }
作用域和闭包
- 自由变量:一个变量在当前作用域内没有被定义,但是被使用了,会向上层作用域逐级寻找,直到顶级作用域没找到报错:XX is not defined
- 闭包:是作用域应用的特殊情况,函数作为参数被传递或者函数作为返回值返回,函数作为返回值的时候,函数定义在局部作用域,在全局作用域调用的时候,局部作用域内定义的自由变量,在定义的地方向上级逐级查找变量的值,而不是在全局作用域处调用的地方向上查找自由变量的值;函数作为参数被传递的时候,函数定义在全局作用域,在局部作为参数被调用,还是在定义的地方去向上级查找自由变量的值。
- 利用闭包隐藏数据, 做一个简单的cache工具
function createCache() { const data = {}; // 闭包中的数据, 被隐藏, 不被外界访问 return { set: function(key, val) { data[key] = val; }, get: function(key) { return data[key]; } } } const c = createCache(); c.set('a', 100); console.log(c.get('a'));
- this指向:this的指向是由调用的时候决定的,不是由定义的时候决定的:
- 在普通函数中:window
- 在对象方法中被调用:指向对象本身
- 通过call、apply、bind:根据传入的值来决定
- 在class中:当前实例本身
- 在箭头函数中:箭头函数没有this,需要向上级作用域的this寻找
- 写一个bind方法:
Function.prototype.bind1 = function () { // **将参数列表拆解为数组 const args = Array.prototype.slice.call(arguments); // 获取this ( 数组第一项 ) const this1 = args.shift(); // fn1.bind( ... )中的 fn1 const self = this; // 返回一个函数 return function() { return self.apply(this1, args); }; }
- 写一个call方法:
Function.prototype.myCall = function (ctx, ...args) { if (typeof this !== 'function') { throw new Error('not a function') } let fn = Symbol(); ctx = ctx || window; ctx[fn] = this; ctx[fn](...args); delete ctx[fn]; } function fn1(a, b) { console.log(this.name); console.log(a, b); }
- 写一个apply方法:
Function.prototype.myApply = function (ctx, args = []) { if (typeof this !== 'function') { throw new Error('not a function') } let fn = Symbol(); ctx = ctx || window; if(!(args instanceof Array)){ throw new TypeError('请输入数组作为参数'); } ctx[fn] = this; ctx[fn](...args); delete ctx[fn]; }
异步
- 如何理解JS是单线程语言?
- JS主线程在同一时间只能执行一个任务,而像网络请求、定时器等需要耗时的任务,我们浏览器遇到这些不能等待,会将耗时的任务放在队列中,等待主线程的任务执行完,再执行。这种异步的方式不阻塞代码的执行。
- 什么是异步编程?
- JS是单线程语言,主线程只能同一时间执行一个任务,待主线程执行完再去任务队列里面轮询任务,在执行任务期间,中间进来的任务都是排列在任务队列里。代码间有相互依赖关系,要求任务队列按照一定的次序排列,大量会用到嵌套来解决这个问题,但嵌套会增加代码复杂性,甚至产生“回调地狱”。ES6的promise语法是一种异步编程机制,解决大量的回调的问题。
- Promise:
- 类型:Function
- 参数:执行器函数,一般为两个函数参数,用于控制期约的状态转换。
- 期约的状态:
- pending:可以转化为兑现或者拒绝,一旦落定不可更改;
- fulfilled/resolved
- rejected
- 传入的参数为一个promise对象,其兑现和拒绝的状态会被保留 ,向后传递,期约状态改变,不会再被重设,不可逆不可撤销;
- then返回的也是一个promise,链式,成对的promise-then关系,下个then处理上一个返回的promise;
- then/catch默认返回的是解决状态的promise
- 最后放catch,统一对前面的错误进行处理
- 用promise实现图片加载的函数:
function ajax(url) { const p = new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('get', url, true); xhr.onreadystatechange = function () { if(xhr.readyState === 4) { if(xhr.status === 200) { resolve( JSON.parse(xhr.responseText) ) } else if (xhr.status === 404) { reject(new Error('404 not found')) } } } xhr.send(null); }) return p; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!