js 面试题总结

js 面试题总结

1、实现 子元素 在父元素中垂直居中的方式

有多种方法可以实现子元素在父元素中垂直居中的方式,以下是其中几种常用的方法:

  1. 使用 Flexbox 布局:

父元素设置为 flex 容器,并使用 align-items 属性将子元素垂直居中。

.parent {
  display: flex;
  align-items: center; /* 子元素垂直居中 */
}
  1. 使用表格布局:

将父元素设置为 table,子元素设置为 table-cell,并使用 vertical-align 属性将子元素垂直居中。

.parent {
  display: table;
}

.child {
  display: table-cell;
  vertical-align: middle; /* 子元素垂直居中 */
}
  1. 使用绝对定位和 transform:

使用绝对定位将子元素相对于父元素居中,结合 transform 属性进行位移变换。

.parent {
  position: relative;
}

.child {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%); /* 子元素垂直居中 */
}
  1. 使用 Grid 布局:

父元素设置为 grid 容器,并使用 place-items 属性将子元素垂直居中。

.parent {
  display: grid;
  place-items: center; /* 子元素垂直居中 */
}

以上是常用的几种方法,选择适合你项目需求和浏览器兼容性的方式来实现子元素在父元素中垂直居中。

2、实现 子元素 在父元素中水平 垂直居中的方式

1.可以使用 Flexbox 布局。以下是一种基本的方法:

.parent {
  display: flex;
  justify-content: center; /* 子元素水平居中 */
  align-items: center; /* 子元素垂直居中 */
}

以上代码将父元素设置为 flex 容器,使用 `justify-content` 属性将子元素水平居中,使用 `align-items` 属性将子元素垂直居中。

2.使用绝对定位和 transform:

.parent {
  position: relative;
}

.child {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%); /* 子元素水平垂直居中 */
}

3.使用表格布局:

将父元素设置为 table,子元素设置为 table-cell,并使用 vertical-align 和 text-align 属性控制对齐方式。

.parent {
  display: table;
  width: 100%;
  height: 100%;
}

.child {
  display: table-cell;
  vertical-align: middle; /* 子元素垂直居中 */
  text-align: center; /* 子元素水平居中 */
}

4.使用 Grid 布局:

.parent {
  display: grid;
}

.child {
  justify-self: center; /* 子元素水平居中 */
  align-self: center; /* 子元素垂直居中 */
}

3、描述 Keepealive 的作用,有哪些钩子函数,如何控制组件级存列表?

KeepAlive 组件是 Vue.js 中的一个内置组件,用于缓存和复用组件实例。它可以将动态组件进行缓存,在组件切换时保留其状态,提高应用性能。

作用

  1. 缓存组件状态:使用 KeepAlive 包裹的组件在切换时会被缓存,保留组件的状态,包括数据、DOM 状态以及生命周期状态。
  2. 复用组件实例:每次切换到被 KeepAlive 包裹的组件时,并不会重新创建组件实例,而是直接使用之前缓存的组件实例。

常用的钩子函数:

  1. activated:在组件被激活时调用,每次切换到该组件时都会触发。可以用来执行组件激活时需要进行的逻辑操作,例如数据加载或动画效果的启动。
  2. deactivated:在组件失活时调用,每次从该组件切换出去时都会触发。可以用来执行组件失活时需要进行的清理操作,例如取消定时器或停止正在进行的动画。

控制组件级存储:

可以通过 的特殊属性 includeexclude 控制具体哪些组件需要被缓存,以及哪些组件不需要被缓存。

  • include:指定要缓存的组件名称数组,只有包含在该数组中的组件才会被缓存。
  • exclude:指定不需要缓存的组件名称数组,这些组件将不会被缓存。

示例如下:

<keep-alive :include="['ComponentA', 'ComponentB']" :exclude="['ComponentC']">
  <router-view>router-view>
keep-alive>

以上代码中,只有名为 “ComponentA” 和 “ComponentB” 的组件会被缓存,而名为 “ComponentC” 的组件不会被缓存。其他组件则按需创建和销毁。

使用 includeexclude 可以灵活地控制组件级别的缓存策略,根据具体需求来决定哪些组件需要保持状态并进行复用。

4、请写出判断对象是数组的三个方法

判断对象是否为数组的三个常用方法如下:

  1. 使用 Array.isArray() 方法:
if (Array.isArray(obj)) {
  // obj 是一个数组
}
  1. 使用 instanceof 运算符:
if (obj instanceof Array) {
  // obj 是一个数组
}
  1. 使用 Object.prototype.toString.call() 方法:
if (Object.prototype.toString.call(obj) === '[object Array]') {
  // obj 是一个数组
}

这些方法都可以判断给定的对象是否为数组。对于这三种方法,推荐使用 Array.isArray(),因为它是专门用于判断对象是否为数组的方法,且在不支持ES5的环境中也能正常工作。

5、请说出下面代码的执行结果

var myObj1 = {
  name: '小王',
  myAge: this.age,
  sayName: function(add, front) {
    console.log(this.name + '今年' + this.age + '在' + add + '做' + front);
  }
};

var heros = {
  name: '小张',
  age: '20'
};
myObj1.sayName.call(heros, '上海', '前端');
myObj1.sayName.apply(heros,['上海','前端']);
myObj1.sayName.bind(heros)('上海','前端');
myObj1.sayName.bind(heros,'上海','前端')();
myObj1.sayName.bind(heros,['上海','前端'])()



 小张今年20在上海做前端
 小张今年20在上海做前端
 小张今年20在上海做前端
 小张今年20在上海做前端
 小张今年20在上海,前端做undefined
 
 考察的 

下面是对每个调用方法的说明:
1、 `myObj1.sayName.call(heros, '上海', '前端')`:使用 `call` 方法调用 `sayName` 方法,并将 `heros` 对象作为方法的上下文(`this`)绑定,同时传递 `'上海'` 和 `'前端'` 作为参数。 立即执行

2 `myObj1.sayName.apply(heros, ['上海', '前端'])`:使用 `apply` 方法调用 `sayName` 方法,并将 `heros` 对象作为方法的上下文(`this`)绑定,同时以** 数组 **形式传递 `['上海', '前端']` 作为参数。  立即执行
3、myObj1.sayName.bind(heros)('上海','前端'); 将 heros 作为参数 传入 bind 方法,然后在后面的调用中又依次传入 上海、前端
4、、 `myObj1.sayName.bind(heros, '上海', '前端')()`:使用 `bind` 方法创建一个新的函数,并将 `heros` 对象作为方法的上下文(`this`)绑定,同时传递 `'上海'` 和 `'前端'` 作为参数。然后立即调用新创建的函数。
5、myObj1.sayName.bind(heros,['上海','前端'])()  将heros 作为改变this 指向的值传入,再把 ['上海','前端'] 这个数组作为第一个替换值传入, 所以 add =‘上海,前端’  ,front =undefined 

知识点:

在 JavaScript 中,`call`、`apply` 和 `bind` 都是用于改变函数的执行上下文(即函数内部的 `this` 指向)的方法。它们的主要区别在于参数的传递方式和是否立即执行函数。

1. `call` 方法:
   - 语法:`function.call(thisArg, arg1, arg2, ...)`。
   - 作用:`call` 方法调用一个函数,并将指定的对象作为函数的执行上下文(`this`)。可以通过 `call` 方法实现继承、借用其他对象的方法或更改函数内部的上下文。
   - 参数:
     - `thisArg`:被绑定到函数的执行上下文(`this`)的对象。
     - `arg1`, `arg2`, ...:函数调用时所需的参数列表。
   - 返回值:函数的返回结果。

2. `apply` 方法:
   - 语法:`function.apply(thisArg, [argsArray])`。
   - 作用:`apply` 方法调用一个函数,并将指定的对象作为函数的执行上下文(`this`)。与 `call` 方法类似,不同之处在于参数的传递方式,`apply` 使用 ·数组·来传递参数。
   - 参数:
     - `thisArg`:被绑定到函数的执行上下文(`this`)的对象。
     - `[argsArray]`:一个包含函数调用时所需参数的数组。
   - 返回值:函数的返回结果。

3. `bind` 方法:
   - 语法:`function.bind(thisArg, arg1, arg2, ...)`。
   - 作用:`bind` 方法.创建. 一个新的函数,该函数和原来的函数具有相同的函数体,但执行上下文(`this`)永久地被绑定到 `bind` 方法的第一个参数指定的对象。
   - 参数:
     - `thisArg`:被绑定到新函数的执行上下文(`this`)的对象。
     - `arg1`, `arg2`, ...:在调用新函数时要传递给原函数的固定参数。
   - 返回值:返回一个绑定了执行上下文和固定参数的新函数,并不会立即执行。

在给定的代码示例中,我们使用了 `call`、`apply` 和 `bind` 来改变 `myObj1.sayName` 函数的执行上下文,并传递了额外的参数。

- `myObj1.sayName.call(heros, '上海', '前端')`:调用 `sayName` 函数,使其以 `heros` 对象作为执行上下文(`this`),并传递 `'上海'` 和 `'前端'` 作为参数。结果会打印出类似于 "小张今年20在上海做前端" 的内容。
- `myObj1.sayName.apply(heros, ['上海', '前端'])`:类似于 `call` 方法,但是参数通过数组进行传递。
- `myObj1.sayName.bind(heros, '上海', '前端')()`:创建一个新的函数,并将 `heros` 对象作为执行上下文(`this`),同时传递 `'上海'` 和 `'前端'` 作为参数。由于使用了括号 `()`,新函数会立即执行,并打印出相应的内容。

需要注意的是,在使用 `call`、`apply` 和 `bind` 时,要确保参数和参数列表的正确配对,并根据具体需求选择合适的方法来改变函数的执行上下文。

6、过滤器如何使用? 请写一个数字保两个小数的过滤器

在 JavaScript 中,你可以使用过滤器(Filter)来对数据进行格式化和处理。过滤器通常用于将原始数据转化为需要的形式,比如在显示数据时对数字进行格式化。

下面是一个示例代码,实现了一个保留两位小数的数字过滤器:

// 定义数字保留两位小数的过滤器
function twoDecimalFilter(value) {
  if (typeof value !== 'number') return value; // 非数字类型直接返回

  return value.toFixed(2); // 使用toFixed方法保留两位小数
}

// 示例使用
let number1 = 12.3456;
let number2 = 3.7;
let string = 'abc';

console.log(twoDecimalFilter(number1)); // 输出 "12.35"
console.log(twoDecimalFilter(number2)); // 输出 "3.70"
console.log(twoDecimalFilter(string));  // 输出 "abc"

上述代码中,twoDecimalFilter 是一个自定义的过滤器函数。它接受一个参数 value,判断该值是否为数字类型,如果是,则使用 toFixed 方法将其保留两位小数并返回;如果不是数字,则直接返回原值。

在使用过滤器时,只需调用过滤器函数并传入要格式化的数据即可。在上面的示例中,我们分别对 number1number2string 进行过滤,并输出结果。

请注意,在实际开发中,可能会使用框架或库来提供更便捷和灵活的过滤器机制,比如在 Vue.js 中使用过滤器。以上示例仅演示了一个简单的自定义过滤器的实现方式。

上述要求用vue的过滤器实现

使用 Vue 的过滤器可以更方便地对数据进行格式化和处理。下面是一个使用 Vue 过滤器的示例,实现保留两位小数的数字过滤器:

html复制代码

{{ number1 | twoDecimal }}

{{ number2 | twoDecimal }}

{{ string | twoDecimal }}

在上述代码中,我们创建了一个 Vue 实例,并在该实例中定义了一个过滤器 twoDecimal。在模板中,通过使用管道操作符 | 将要过滤的数据与过滤器名称相连,在输出时会自动应用过滤器。

在这个示例中,number1number2string 是 Vue 实例的数据属性,分别代表要过滤的数字和字符串。通过 {{ number | twoDecimal }} 的形式,将数值绑定到对应的模板插值处,同时应用名为 twoDecimal 的过滤器,从而实现保留两位小数的效果。

运行以上代码,你将看到页面上输出的数值已按照过滤器的设定进行了格式化。

7、在vue里面 什么是自定义指令,请注册一个全局的自定义指令。

在 Vue 中,自定义指令(Custom Directive)是一种扩展 Vue 的能力,用于对 DOM 元素进行低层次的操作和交互。通过自定义指令,我们可以直接操作 DOM、添加事件监听器、进行样式操作等。

要注册一个全局的自定义指令,你可以使用 Vue.directive 方法。下面是一个示例代码,演示如何注册一个名为 my-directive 的全局自定义指令:

<div id="app">
  <input v-my-directive />
div>

<script src="https://cdn.jsdelivr.net/npm/[email protected]">script>
<script>
// 注册全局自定义指令
Vue.directive('my-directive', {
  bind: function(el, binding, vnode) {
    // 指令绑定时的处理逻辑
    el.style.backgroundColor = 'yellow';
    el.addEventListener('focus', function() {
      el.style.color = 'red';
    });
  },
  inserted: function(el, binding, vnode) {
    // 指令插入到 DOM 中的处理逻辑
  },
  update: function(el, binding, vnode, oldVnode) {
    // 组件更新时的处理逻辑
  },
  unbind: function(el, binding, vnode) {
    // 指令解绑时的处理逻辑
    el.removeEventListener('focus');
  }
});

// 创建 Vue 实例
new Vue({
  el: '#app',
});
script>

在上述代码中,我们使用 Vue.directive 方法注册了一个名为 my-directive 的全局自定义指令。该指令包含了几个生命周期钩子函数,例如 bindinsertedupdateunbind,你可以根据需要实现相应的逻辑。

在示例代码中,bind 钩子函数用来在指令绑定时设置元素的背景颜色为黄色,并添加一个焦点事件监听器,当聚焦到输入框时,将文字颜色设置为红色。

在 Vue 实例的模板中,我们通过 v-my-directive 指令将这个自定义指令应用到了一个 元素上。当 Vue 渲染该模板时,my-directive 指令就会被触发,执行对应的逻辑。

请注意,以上示例代码是注册一个全局自定义指令的基本用法,你可以根据实际需求进行扩展和修改。

8、请说出下面代码的输出结果

const arr1 =[
	{
		id:1,
		name:'name1
	},{
		id:2
		name:'name2
	}
]

const arr2 =[...arr1] // 复制数组,浅拷贝
arr1[0].name='namel-undate' // 会改变复制后的数组
console.log(arr2[0].name)  // 

const obj ={
	a:'aa'
	b:bb'
}
const obj2 = {...obj}  // 复制对象形成新对象
obj.a='aa-update' // 不会影响新对象
console.log(obj.a)
console.log(obj2.a)

对于更新后的代码如下:

const arr1 = [{
  id: 1,
  name: 'name1'
}, {
  id: 2,
  name: 'name2'
}];

const arr2 = [...arr1];
arr1[0].name = 'namel-update';
console.log(arr2[0].name);

与之前相同,arr1是一个包含两个对象元素的数组。通过扩展运算符[...arr1]arr1数组的元素复制给了新数组arr2。同样地,这仍然是浅拷贝,意味着修改arr1中的第一个对象的name属性会影响到arr2中的对应元素。因此,console.log(arr2[0].name)将输出namel-update

const obj = {
  a: 'aa',
  b: 'bb'
};
const obj2 = {...obj};
obj.a = 'aa-update';
console.log(obj.a);
console.log(obj2.a);

在这段代码中,我们使用了对象展开语法{...obj}来将obj对象进行复制。这种方式会创建一个新的对象,并将原始对象中的属性复制到新的对象中。因此,obj2是一个独立的对象,其属性与原始对象obj相同但值不共享。

由于我们将obj对象的a属性值修改为'aa-update',所以console.log(obj.a)将输出'aa-update'。而console.log(obj2.a)将输出原始的属性值'aa',因为obj2是在修改之前复制的对象。

综上所述,使用对象展开语法{...obj}可以实现对象的浅拷贝,并且复制后的对象与原始对象的属性值不共享。

9、介绍下浅拷贝和深拷贝

浅拷贝和深拷贝是两种常用的对象或数组复制方法,它们在复制过程中处理引用类型数据的方式不同。

  1. 浅拷贝:
    • 浅拷贝是指创建一个新对象或数组,将原始对象或数组的值复制到新对象或数组中。当复制引用类型数据时,只复制了引用地址,而不是创建新的独立副本。
    • 浅拷贝后的新对象或数组与原始对象或数组共享相同的引用值,修改其中任何一个的属性或元素会影响到另一个。
    • 常见的浅拷贝方式包括扩展运算符[...arr]{...obj}Object.assign()等。
  2. 深拷贝:
    • 深拷贝是指完全复制一个对象或数组及其所有嵌套的对象或数组,得到一个全新且独立的副本。这样两个对象/数组互不干扰,修改其中一个不会影响另一个。
    • 深拷贝复制了所有层级的数据,包括基本类型和引用类型数据,递归地遍历复制每个对象或数组的属性或元素。
    • 常见的实现深拷贝的方式包括循环递归、JSON序列化与反序列化、使用第三方库(如Lodash或深拷贝工具函数)等。

在选择使用浅拷贝还是深拷贝时,需要根据具体需求和数据结构来决定。如果对象/数组的属性值不包含引用类型数据、或者对共享值的修改无影响,那么可以使用浅拷贝;如果需要完全独立且不受影响地操作复制后的对象/数组,那么应该使用深拷贝。

你可能感兴趣的:(面试,javascript,css,css3)