JavaScript 中的数组对象提供了一系列方法来执行增加、删除、修改和查询操作。这些方法中,有些会直接修改原数组,而另一些则不会改变原数组,而是返回一个新数组或其他值。下面是常用的数组API及其对原数组的影响:

增加元素

  • push() - 在数组的末尾添加一个或多个元素,并返回新的长度。这个方法会修改原数组
  • unshift() - 在数组的开头添加一个或多个元素,并返回新的长度。这个方法会修改原数组
  • splice() - 从数组中添加/删除项目,然后返回被删除的项目。这个方法会修改原数组

删除元素

  • pop() - 删除数组的最后一个元素并返回删除的元素。这个方法会修改原数组
  • shift() - 删除数组的第一个元素并返回删除的元素。这个方法会修改原数组
  • splice() - 同上,可以用于删除元素,并会修改原数组

修改元素

  • splice() - 除了添加和删除,还可以用于替换元素,会修改原数组
  • 直接索引赋值 - 使用索引直接修改数组,如 arr[0] = 'new value';会修改原数组

查询元素

  • indexOf() - 返回在数组中可以找到给定元素的第一个索引,如果不存在,则返回-1。这个方法不修改原数组
  • lastIndexOf() - 返回数组中最后一个(最大索引的)指定元素的索引,如果不存在则返回-1。这个方法不修改原数组
  • includes() - 判断数组是否包含一个指定的值,根据情况返回 true 或 false。这个方法不修改原数组
  • find() - 返回数组中满足提供的测试函数的第一个元素的值,否则返回 undefined。这个方法不修改原数组
  • findIndex() - 返回数组中满足提供的测试函数的第一个元素的索引,否则返回-1。这个方法不修改原数组
  • filter() - 创建一个新数组,其包含通过所提供函数实现的测试的所有元素。这个方法不修改原数组

其他有用的API

  • slice() - 返回一个新数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(不包括 end)。原数组不会被改变,这个方法不修改原数组
  • concat() - 用于合并两个或多个数组。此方法不会改变现有的数组,而是返回一个新数组,这个方法不修改原数组

总结

了解这些方法是否修改原数组非常重要,尤其是在处理大型数据或需要保持原始数据不变的场景中。通常,方法名中带有 “pop”, “push”, “shift”, “unshift”, “splice” 的方法会修改原数组,而 “slice”, “concat”, “find”, “filter” 等方法则返回一个新数组或值,不修改原数组。

在 Vue.js 中,使用 scoped 属性的 <style> 标签能确保 CSS 样式只作用于当前组件的元素,这是通过 Vue 的编译过程自动添加唯一的属性(如 data-v-xxxxxx)来实现的。这种机制确保了组件内的样式不会泄露到全局,从而避免了不同组件间的样式冲突。接下来,我将详细解释这个机制以及如何改写第三方库(如 Element UI)的样式。

CSS Scoped 工作原理

  1. 添加唯一属性: 当你在组件的 <style> 标签中添加 scoped 属性时,Vue 的编译器会自动为每个在模板中的元素添加一个唯一的数据属性(如 data-v-f3f3eg9)。这个属性是随机生成的,确保每个组件实例的样式都是独立的。

  2. 修改CSS选择器: 编译过程中,Vue 会自动修改组件内的 CSS 选择器,让它们与添加了数据属性的HTML元素匹配。例如,.button 变成了 .button[data-v-f3f3eg9]。这样,CSS 规则只会匹配带有相应数据属性的元素。

改写 Element UI 的样式

Element UI 是一个流行的 Vue UI 框架,它的样式默认是全局的。如果你想在使用 scoped CSS 的组件内部改写 Element UI 的样式,直接写 CSS 可能不会生效,因为 scoped 的样式具有更高的特异性。有几种方法可以解决这个问题:

  1. 深度选择器 (>>>/deep/):
    在 Vue 中,如果你需要在一个使用 scoped 的样式表中影响子组件(如来自第三方库的组件),可以使用深度选择器。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    /* Vue 2 使用 >>> */
    .my-component >>> .el-button {
    background-color: red;
    }

    /* Vue 3 使用 /deep/ 或 ::v-deep */
    .my-component /deep/ .el-button {
    background-color: red;
    }

    这种方法允许你穿透 scoped 的边界,改写内部元素的样式。

  2. 不使用 scoped:
    另一个简单的方法是在组件中使用非 scoped<style> 标签。你可以保持全局样式规则的同时,仍然在组件内部维护样式隔离。

    1
    2
    3
    .el-button {
    background-color: red;
    }

    这样的样式会应用到所有 .el-button 元素上,无论它们在哪个组件内。

  3. 内联样式:
    在某些情况下,你也可以直接在元素上使用内联样式来覆盖样式,尤其是在样式变化较少或仅需一次性修改时。

    1
    <el-button style="background-color: red;">按钮</el-button>

使用上述任一方法时,都应谨慎考虑样式的维护性和可扩展性,尤其是在大型项目中。选择最合适的方法取决于你的具体需求和项目的复杂度。

防抖-最后一次

1
2
3
4
5
6
7
8
9
10
11
function debounce(){
let timeout;
return function(){
const context=this,arguments;
clearout(timeout);
timeout=setTimeout((=>{
func.apply(context,args);
},1000))
}

}

节流

1
2
3
4
5
function th(func,limit){
let lst;


}

在JavaScript中,继承是对象和类的一个基本概念,它允许一个对象或类继承另一个对象或类的属性和方法。JavaScript主要是基于原型的语言,但ES6之后,通过类的语法也提供了继承的实现。以下是几种实现继承的常见方式:

1. 原型链继承

原型链继承是JavaScript最基本的继承方式,它通过将子类的原型设置为父类的一个实例来实现继承。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Parent() {
this.name = 'Parent';
this.colors = ['red', 'blue', 'green'];
}

Parent.prototype.getName = function() {
return this.name;
}

function Child() {
this.type = 'Child';
}

// 继承父类
Child.prototype = new Parent();

var child1 = new Child();
console.log(child1.getName()); // Parent
child1.colors.push('yellow');
console.log(child1.colors); // ["red", "blue", "green", "yellow"]

这种方法的主要问题是所有的子类实例都会共享父类构造函数中的引用属性,这可能导致一个实例修改属性后影响到其他所有实例。

2. 借用构造函数继承(经典继承)

这种方法通过在子类构造函数中调用父类构造函数,借助callapply方法来实现继承,可以解决原型链继承中引用类型共享的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Parent(name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}

function Child(name) {
Parent.call(this, name); // 继承父类,并向父类传递参数
this.type = 'Child';
}

var child1 = new Child('child1');
var child2 = new Child('child2');
child1.colors.push('yellow');

console.log(child1.name); // child1
console.log(child2.name); // child2
console.log(child1.colors); // ["red", "blue", "green", "yellow"]
console.log(child2.colors); // ["red", "blue", "green"]

这种方式可以实现多个实例之间属性不共享,但方法都在构造函数中定义,每次创建实例时都会创建一遍方法。

3. 组合继承

组合继承结合了原型链继承和借用构造函数继承的优点,既能继承父类的属性和方法,也能保证不共享引用类型的属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function Parent(name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}

Parent.prototype.getName = function() {
return this.name;
}

function Child(name, type) {
Parent.call(this, name); // 借用构造函数,属性不共享
this.type = type;
}

Child.prototype = new Parent(); // 原型链继承,方法共享
Child.prototype.constructor = Child;

var child1 = new Child('child1', 'type1');
var child2 = new Child('child2', 'type2');
child1.colors.push('yellow');

console.log(child1.getName()); // child1
console.log(child2.getName()); // child2
console.log(child1.colors); // ["red", "blue", "green", "yellow"]
console.log(child2.colors); // ["red", "blue", "green"]

组合继承解决了上述两种方法的缺点,但仍有性能问题,因为父类构造函数总是被调用两次。

4. ES6 类继承

ES6 引入了类(class)和继承(extends)的语法糖,使得基于原型的继承更加易于理解和实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Parent {
constructor(name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}

getName() {
return this.name;
}
}

class Child extends Parent {
constructor(name, type) {
super(name);

// 调用父类的constructor(name)
this.type = type;
}
}

const child1 = new Child('child1', 'type1');
const child2 = new Child('child2', 'type2');
child1.colors.push('yellow');

console.log(child1.getName()); // child1
console.log(child2.getName()); // child2
console.log(child1.colors); // ["red", "blue", "green", "yellow"]
console.log(child2.colors); // ["red", "blue", "green"]

ES6 类继承是最现代和推荐的继承方式,它内部实现仍基于原型链,但提供了更清晰和方便的语法。

这些是JavaScript中实现继承的主要方式,每种方式都有其适用场景和优缺点,开发者可以根据具体需求选择合适的方法。

new

1
2
3
4
5
6
7
8
function new(){
const obj=Object.create(constructor.prototype);

const result=constructor.apply(obj,args);

return result!==null&&(typeof result==='object'||typeof result==='function')

}