14、手写new
在 JavaScript 中,new 关键字用于创建对象实例。实现一个简单的 new 方法可以按照以下步骤进行:
- 创建一个新对象。
- 将该对象的原型链连接到构造函数的原型对象上。
- 将构造函数内部的
this绑定到新对象上,并执行构造函数。 - 如果构造函数返回一个对象,则返回该对象;否则返回创建的新对象。
下面是一个简单的实现:
function myNew(Constructor, ...args) {
// 创建一个新对象,原型为构造函数的原型对象
const obj = Object.create(Constructor.prototype);
// 将构造函数内部的 this 绑定到新对象上,并执行构造函数
const result = Constructor.apply(obj, args);
// 如果构造函数返回了一个对象,则返回该对象;否则返回创建的新对象
return result instanceof Object ? result : obj;
}
使用示例:
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHello = function() {
console.log(`Hello, my name is ${this.name}, I'm ${this.age} years old.`);
};
const person1 = myNew(Person, 'Alice', 30);
person1.sayHello(); // 输出:Hello, my name is Alice, I'm 30 years old.
二面:
1.使用vue手写一个购物车页面
以下是一个简单的 Vue 购物车页面示例,包含商品列表、添加商品到购物车、删除购物车中商品以及计算总价功能:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue Shopping Cart</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1>Vue Shopping Cart</h1>
<!-- 商品列表 -->
<div>
<h2>商品列表</h2>
<ul>
<li v-for="product in products" :key="product.id">
{{ product.name }} - ¥{{ product.price }}
<button @click="addToCart(product)">加入购物车</button>
</li>
</ul>
</div>
<!-- 购物车 -->
<div>
<h2>购物车</h2>
<ul>
<li v-for="item in cart" :key="item.id">
{{ item.name }} - ¥{{ item.price }} - 数量:{{ item.quantity }}
<button @click="removeFromCart(item)">删除</button>
</li>
</ul>
<p>总价:¥{{ getTotalPrice() }}</p>
</div>
</div>
<script>
new Vue({
el: '#app',
data: {
products: [
{ id: 1, name: '商品1', price: 10 },
{ id: 2, name: '商品2', price: 20 },
{ id: 3, name: '商品3', price: 30 }
],
cart: []
},
methods: {
addToCart(product) {
const index = this.cart.findIndex(item => item.id === product.id);
if (index !== -1) {
this.cart[index].quantity++;
} else {
this.cart.push({ ...product, quantity: 1 });
}
},
removeFromCart(item) {
const index = this.cart.findIndex(cartItem => cartItem.id === item.id);
if (index !== -1) {
if (this.cart[index].quantity > 1) {
this.cart[index].quantity--;
} else {
this.cart.splice(index, 1);
}
}
},
getTotalPrice() {
return this.cart.reduce((total, item) => total + (item.price * item.quantity), 0);
}
}
});
</script>
</body>
</html>
这个示例包含了商品列表和购物车两个部分,商品列表中每个商品都有一个“加入购物车”按钮,点击后将商品添加到购物车中。购物车中每个商品后面有一个“删除”按钮,点击后可以从购物车中移除该商品。页面底部显示了购物车中所有商品的总价。
2.rgba颜色转换
RGBA 颜色表示一种使用红、绿、蓝和透明度(alpha)通道来定义颜色的方式。RGBA 中的每个通道的值的范围通常是 0 到 255(或 0 到 1),表示颜色的强度或不透明度。
如果要进行颜色值之间的转换,可以使用下面的方法:
-
RGB 到 RGBA 转换: 如果已经有了一个 RGB 颜色值,要将其转换为 RGBA 格式,只需在 RGB 值后面添加一个表示透明度的数值即可。例如,假设你有一个纯红色(255, 0, 0)的颜色值,你可以添加一个表示完全不透明的 alpha 值(1)来转换为 RGBA 格式:rgba(255, 0, 0, 1)。
-
RGBA 到 RGB 转换: 如果要将 RGBA 格式的颜色值转换为 RGB 格式,只需忽略透明度通道即可。例如,如果有一个表示半透明红色的颜色值 rgba(255, 0, 0, 0.5),你可以忽略最后的透明度值,得到 RGB 格式的值:rgb(255, 0, 0)。
-
十六进制到 RGBA 转换: 如果有一个十六进制格式的颜色值,要转换为 RGBA 格式,可以使用一些工具或者编写函数来实现。通常,十六进制的颜色值包含六个字符,前两个表示红色通道,中间两个表示绿色通道,最后两个表示蓝色通道。要将其转换为 RGBA 格式,只需将每个通道的十六进制值转换为十进制,然后添加一个透明度值即可。
下面是一个将十六进制颜色值转换为 RGBA 格式的 JavaScript 函数示例:
function hexToRgba(hex, alpha) {
let r = parseInt(hex.slice(1, 3), 16);
let g = parseInt(hex.slice(3, 5), 16);
let b = parseInt(hex.slice(5, 7), 16);
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
}
// 使用示例
const hexColor = '#ff0000'; // 红色
const alpha = 0.5; // 半透明度
const rgbaColor = hexToRgba(hexColor, alpha);
console.log(rgbaColor); // 输出 rgba(255, 0, 0, 0.5)
这个函数接受一个十六进制颜色值和一个透明度值作为参数,返回对应的 RGBA 格式的颜色值。
3.实现一个三角形方法
实现一个绘制三角形的方法通常需要使用图形绘制库或者通过原生的 Canvas API 进行绘制。下面是一个使用 Canvas API 绘制三角形的示例代码:
function drawTriangle(context, x1, y1, x2, y2, x3, y3) {
context.beginPath();
context.moveTo(x1, y1);
context.lineTo(x2, y2);
context.lineTo(x3, y3);
context.closePath();
context.stroke();
}
// 获取 Canvas 元素
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// 绘制三角形
drawTriangle(ctx, 100, 100, 200, 200, 300, 100);
在这个示例中,drawTriangle 函数接受一个 Canvas 上下文对象 context,以及三个顶点的坐标 (x1, y1)、(x2, y2) 和 (x3, y3)。函数内部使用 beginPath() 开始一个新的路径,然后通过 moveTo() 方法移动到第一个顶点,使用 lineTo() 方法连接三个顶点,最后通过 closePath() 封闭路径,并通过 stroke() 方法绘制线条。
4.有一个忘了
5.说一下axios的理解,说一下fetch和ajax的区别,请求方式上有什么不同
Axios 的理解:
Axios 是一个基于 Promise 的 HTTP 客户端,用于在浏览器和 Node.js 中进行 HTTP 请求。它提供了许多强大的功能,例如支持异步请求、拦截请求和响应、转换请求和响应数据等。Axios 支持在浏览器中使用 XMLHttpRequests(XHR)和在 Node.js 中使用 HTTP,因此它可以用于浏览器端和服务器端的数据通信。Axios 的主要特点包括:
- 简单易用:Axios 提供了简洁的 API,易于使用,并且可以方便地发送各种类型的 HTTP 请求。
- 支持 Promise:Axios 基于 Promise 实现异步请求,使得处理异步操作更加简单和可靠。
- 拦截器:Axios 允许你在发送请求和响应之前和之后执行自定义逻辑,通过拦截器可以实现请求的统一处理、错误处理、请求/响应日志等功能。
- 支持取消请求:Axios 允许取消未完成的请求,防止不必要的请求发送,减少资源消耗。
- CSRF 保护:Axios 提供了内置的 CSRF 保护机制,可以轻松地防止跨站请求伪造攻击。
- 浏览器兼容性:Axios 支持主流的浏览器,并且可以使用一些 polyfill 来提供对不支持 Promise 的浏览器的支持。
Fetch 和 Ajax 的区别:
-
API 设计:
- Fetch 使用了 Promise API,基于原生的
fetch()函数,提供了链式调用的语法。 - Ajax 是一种基于 XMLHttpRequest(XHR)对象的传统的 JavaScript 技术,通常需要借助第三方库(如 jQuery、Axios 等)来简化使用。
- Fetch 使用了 Promise API,基于原生的
-
语法差异:
- Fetch 使用
fetch()函数发送请求,返回一个 Promise 对象,通过链式调用 then 方法来处理异步操作。 - Ajax 使用 XMLHttpRequest 对象发送请求,需要手动设置请求头、请求体等参数,并通过事件监听器来处理异步操作。
- Fetch 使用
-
功能支持:
- Fetch 更加现代化,提供了更丰富和强大的功能,如请求和响应的流处理、请求的取消、更强大的错误处理等。
- Ajax 虽然简单易用,但功能相对有限,需要依赖第三方库来提供一些高级功能。
-
浏览器兼容性:
- Fetch 在较新的浏览器中得到了广泛的支持,但在一些老旧的浏览器中可能不兼容或需要 polyfill。
- Ajax 作为传统的技术,具有较好的兼容性,可以在几乎所有的浏览器中使用。
请求方式上的不同:
- 无论是 Axios、Fetch 还是 Ajax,它们都支持多种请求方式,如 GET、POST、PUT、DELETE 等,区别主要体现在 API 的使用方式和语法上,而不是请求方式上的不同。
6.window.onload和document.ready()
window.onload 和 document.ready() 都是用于在页面加载完成后执行 JavaScript 代码的方法,但它们有一些区别:
- window.onload:
window.onload是一个事件,在整个页面(包括所有资源)加载完成后触发。- 当页面上所有内容(包括图片、样式表、脚本等)都已完全加载并且浏览器已经创建好了 DOM 树后,
window.onload事件才会被触发。 - 如果页面包含大量资源或者资源较大,可能会导致
window.onload事件的触发延迟。
示例:
window.onload = function() {
// 在整个页面加载完成后执行的代码
};
- document.ready():
document.ready()是 jQuery 中的一个方法,用于在 DOM 树构建完成后即可执行 JavaScript 代码。- 与
window.onload不同,document.ready()不需要等待所有资源加载完成,只需等待 DOM 树构建完成即可执行。 - 由于
document.ready()只需等待 DOM 构建完成,所以它的执行速度比window.onload快,可以提升用户体验。
示例:
$(document).ready(function() {
// 在 DOM 构建完成后即可执行的代码
});
总结:
- 如果需要在页面的所有资源(包括图片、样式表、脚本等)都加载完成后执行代码,则使用
window.onload。 - 如果只需要在 DOM 树构建完成后即可执行代码,则使用
document.ready()或其简写形式$(function() {})。
7.捕获,冒泡,事件对象,事件代理
这些都是 JavaScript 中与事件处理相关的重要概念:
-
捕获(Capture):
- 事件捕获是指从外向内逐层触发事件的过程。
- 在捕获阶段,事件从根节点开始逐级向下传播至触发事件的目标元素。
- 捕获阶段主要用于预处理事件,但在实践中很少使用。
-
冒泡(Bubbling):
- 事件冒泡是指从内向外逐层触发事件的过程。
- 在冒泡阶段,事件从触发事件的目标元素开始向上传播至根节点。
- 冒泡阶段是事件处理的主要阶段,大多数情况下我们会在冒泡阶段处理事件。
-
事件对象(Event Object):
- 事件对象是在事件发生时被创建的,它包含了与事件相关的信息。
- 可以通过事件处理函数的参数来访问事件对象,通常使用
event或e来表示。 - 事件对象提供了许多属性和方法,可以用于获取事件的类型、目标元素、触发位置等信息。
-
事件代理(Event Delegation):
- 事件代理是一种设计模式,通过将事件绑定到父元素而不是每个子元素上,来管理子元素的事件。
- 当子元素触发事件时,事件会冒泡到父元素,并在父元素上触发事件处理函数。
- 通过事件代理,可以减少事件处理函数的数量,提高性能,特别适用于动态生成的子元素或大量相似元素的情况。
综上所述,捕获和冒泡是事件传播的两个阶段,事件对象提供了与事件相关的信息,事件代理是一种优化技术,可以简化事件处理逻辑并提高性能。
8.eventTarget和currentTarget区别
在事件处理函数中,event.target 和 event.currentTarget 是两个常用的属性,它们表示事件的目标和当前目标。它们之间的区别如下:
-
event.target:
event.target属性返回触发事件的元素,即事件最初发生的目标元素。- 如果事件是由子元素触发的(如点击了按钮内部的文本),则
event.target会返回子元素。
-
event.currentTarget:
event.currentTarget属性返回绑定事件处理函数的当前元素,即事件的当前目标元素。- 不论事件是由子元素还是父元素触发,
event.currentTarget始终指向绑定事件处理函数的元素。
举例来说,考虑以下 HTML 结构:
<div id="parent">
<button id="child">Click me</button>
</div>
如果点击按钮内部的文本,则事件会冒泡至父元素 <div id="parent">,此时:
event.target返回按钮元素<button id="child">Click me</button>。event.currentTarget返回父元素<div id="parent">。
总之,event.target 表示触发事件的元素,而 event.currentTarget 表示绑定事件处理函数的当前元素。
9.script 的defer async 和什么都不加的区别
在HTML中,<script> 标签的 defer、async 和不加任何属性的区别如下:
-
不加任何属性:
- 默认情况下,浏览器会立即下载并执行
<script>标签中的 JavaScript 代码,同时阻塞页面的渲染,直到脚本执行完成。 - 如果页面中有多个
<script>标签,它们会按照在文档中的顺序依次执行。
- 默认情况下,浏览器会立即下载并执行
-
defer:
- 带有
defer属性的<script>标签表示延迟执行脚本,即脚本会在文档解析完成后才执行,但是会在DOMContentLoaded事件触发之前执行。 - 如果页面中有多个带有
defer属性的脚本,它们会按照它们在文档中出现的顺序依次执行。
- 带有
-
async:
- 带有
async属性的<script>标签表示异步执行脚本,即脚本的下载和执行是异步进行的,不会阻塞页面的解析和渲染。 - 如果页面中有多个带有
async属性的脚本,它们的执行顺序是不确定的,谁先下载完谁先执行。
- 带有
总的来说:
- 如果需要保证脚本的执行顺序,可以使用
defer属性,它会按照文档中的顺序依次执行。 - 如果不关心脚本的执行顺序,并且希望尽快下载和执行脚本以减少页面加载时间,可以使用
async属性,但要注意脚本之间可能存在依赖关系。 - 如果希望脚本立即执行并且需要保证它们的执行顺序,可以不加任何属性。
10.项目之类的
作者:好运来好运
链接:www.nowcoder.com/feed/main/d…
来源:牛客网