发表于: 2019-11-19 07:33:33

0 932


js基础 - 续1

构造函数和操作符 "new"

构造函数

构造函数在技术上是常规函数。不过有两个约定:

  1. 他们首先用大写字母命名。
  2. 它们只能用 "new" 操作符来执行。

function User(name) {

  this.name = name;

  this.isAdmin = false;

}

let user = new User("Jack");

alert(user.name); // Jack

alert(user.isAdmin); // false

当一个函数作为 new User(...)执行时,它执行以下步骤:

  1. 一个新的空对象被创建并分配给 this
  2. 函数体执行。通常它会修改 this,为其添加新的属性。
  3. 返回 this 的值。

换句话说,new User(...) 做类似的事情:

function User(name) {

  // this = {};(隐式创建)

  // 添加属性到 this

  this.name = name;

  this.isAdmin = false;

  // return this;(隐式返回)

}

这是构造函数的主要目的 — 实现可重用的对象创建代码

 从技术上讲,任何函数都可以用作构造函数。即:任何函数都可以运行 new,它会执行上面的算法。 “首字母大写”是一个共同的约定,以明确表示一个函数将被使用 new 运行。

如果一个函数返回一个对象,那么 new 返回那个对象而不是 this

new function() { … }

如果我们有许多关于创建单个复杂对象的代码行,我们可以将它们封装在构造函数中,如下所示:

let user = new function() {

  this.name = "John";

  this.isAdmin = false;

  // ...用户创建的其他代码

  // 也许是复杂的逻辑和陈述

  // 局部变量等

};

构造函数不能被再次调用,因为它不保存在任何地方,只是被创建和调用。所以这个技巧的目的是封装构建单个对象的代码,而不是将来重用。

构造函数中的方法

使用构造函数来创建对象会带来很大的灵活性。通过构造函数的参数传递定义构造对象。

当然,我们不仅可以将属性添加到 this 中,而且还可以添加方法。

总结

  • 构造函数或简言之,就是常规函数,但构造函数有个共同的约定,命名它们首字母要大写。
  • 构造函数只能使用 new 来调用。这样的调用意味着在开始时创建空的 this,并在最后返回填充的对象。

我们可以使用构造函数来创建多个类似的对象。

JavaScript 为许多内置的对象提供了构造函数:比如日期 Date,设置集合 Set 以及其他。

数据类型

数组方法

添加/移除数组元素

从开头或结尾添加删除元素的方法:

  • arr.push(...items) — 从结尾添加元素,
  • arr.pop() — 从结尾提取元素,
  • arr.shift() — 从开头提取元素,
  • arr.unshift(...items) — 从开头添加元素,

如何从数组中删除元素?

数组是对象,所以我们可以尝试使用 delete

let arr = ["I", "go", "home"];

delete arr[1]; // remove "go"

alert( arr[1] ); // undefined

// now arr = ["I",  , "home"];

alert( arr.length ); // 3

元素被删除,但数组仍然有 3 个元素,我们可以看到 arr.length == 3

这很正常,因为 delete obj.key 是通过 key 来移除对应的值。但是对于数组,我们通常希望剩下的元素移除并释放占用的位置,得到一个更短的数组。

所以应该使用特殊的方法。

arr.splice(str) 方法可以说是数组界的瑞士军刀。它可以做所有事情:添加,删除和插入元素。

语法是:arr.splice(index, deleteCount, elem1, ..., elemN)

index 必需。从第几个元素开始,负值从数组结尾处规定位置。(-1为倒数第二位)

deleteCount 必需。要删除的项目数量。如果设置为 0,则不会删除项目。(可实现指定处添加元素)

elem1, ..., elemN 可选

从 index 开始:删除 deleteCount 个元素并在当前位置插入 elem1, ..., elemN。最后返回已删除元素的数组。

我们也可以看到 splice 返回已删除元素的数组:

let arr = ["I", "study", "JavaScript", "right", "now"];

// remove 2 first elements

let removed = arr.splice(0, 2);

alert( removed ); // "I", "study" <-- 被删除元素的数组

slice

arr.slice 方法比 arr.splice 简单得多。

语法是:arr.slice(start, end)

它从所有元素的开始索引 "start" 复制到 "end" (不包括 "end") 返回一个新的数组。start 和 end 都可以是负数,在这种情况下,从末尾计算索引。

它和字符串的 str.slice 有点像,就是把子字符串替换成子数组。

let str = "test";

let arr = ["t", "e", "s", "t"];

alert( str.slice(1, 3) ); // es

alert( arr.slice(1, 3) ); // e,s

alert( str.slice(-2) ); // st

alert( arr.slice(-2) ); // s,t

concat

arr.concat 将数组与其他数组和/或元素结合在一起。

语法:arr.concat(arg1, arg2...)

它接受任意数量的参数 — 数组或值。

结果是一个包含arrarg1arg2等元素的新数组。

如果参数是一个数组或具有 Symbol.isConcatSpreadable 属性,则其所有元素都将被复制。否则,复制参数本身。

通常,它只复制数组中的元素(“扩展”它们)。其他对象,即使它们看起来像数组一样,仍然作为一个整体添加:

let arr = [1, 2];

let arrayLike = {

  0: "something",

  length: 1

};

alert( arr.concat(arrayLike) ); // 1,2,[object Object]

//[1, 2, arrayLike]

…但是,如果类似数组的对象具有 Symbol.isConcatSpreadable 属性,将替换其元素:

let arr = [1, 2];

let arrayLike = {

  0: "something",

  1: "else",

  [Symbol.isConcatSpreadable]: true,

  length: 2

};

alert( arr.concat(arrayLike) ); // 1,2,something,else

查询数组

这些是在数组中查询某些内容的方法。

indexOf/lastIndexOf 和 includes

arr.indexOfarr.lastIndexOf 和 arr.includes 方法与字符串操作具有相同的语法,只不过这里是对数组元素而不是字符进行操作:

  • arr.indexOf(item, from) 从索引 from 查询 item,如果找到返回索引,否则返回 -1
  • arr.lastIndexOf(item, from) — 和上面相同,只是从尾部开始查询。
  • arr.includes(item, from) — 从索引 from 查询 item,如果找到则返回 true

find 和 findIndex

想象一下,我们有一个对象数组。我们如何找到具有特定条件的对象?

这时可以用 arr.find 方法。

语法:

let result = arr.find(function(item, index, array) {

  // 如果查询到返回 true

});

该函数对数组中的每个元素重复调用:

  • item 是元素。
  • index 是它的索引。
  • array 是数组本身。

如果它返回true,则查询停止,返回 item。如果没有查询到,则返回 undefined

例如,我们有一组用户,每个用户都有 id 和 name 字段。让我们找到一个 id == 1

let users = [

  {id: 1, name: "John"},

  {id: 2, name: "Pete"},

  {id: 3, name: "Mary"}

];

let user = users.find(item => item.id == 1);

alert(user.name); // John

注意在这个例子中我们传给了 find 一个单参数函数 item => item.id == 1。其他参数 find 很少使用。

与 arr.findIndex 方法本质上是相同的,但它返回找到元素的索引而不是元素本身。

filter

find 方法查询的是使函数返回 true 的第一个元素。

如果需要匹配的有很多,我们可以使用 arr.filter(fn)

语法与 find 大致相同,但是它返回的是所有匹配元素组成的数组:

let results = arr.filter(function(item, index, array) {

  // 在元素通过过滤器时返回 true

});

let users = [

  {id: 1, name: "John"},

  {id: 2, name: "Pete"},

  {id: 3, name: "Mary"}

];

// 返回前两个用户的数组

let someUsers = users.filter(item => item.id < 3);

alert(someUsers.length); // 2

转换数组

本节介绍转换或重新排序数组的方法。

map

arr.map 方法是最有用和经常使用的方法之一。

语法:

let result = arr.map(function(item, index, array) {

  // 返回新值而不是当前元素

})

它对数组中每个元素调用函数并返回符合结果的数组。

例如,在这里我们将每个元素转换为它的字符串长度:

let lengths = ["Bilbo", "Gandalf", "Nazgul"].map(item => item.length)

alert(lengths); // 5,7,6

sort(fn)

arr.sort 方法对数组进行排序

语法:

let arr = [ 1, 2, 15 ];

// 该方法重新排列 arr 的内容(并返回它)

arr.sort();

alert( arr );  // 1, 15, 2

顺序变成了 1, 15, 2。不对,但为什么呢?

这些元素默认情况下按字符串排序。

从字面上看,所有元素都被转换为字符串,然后进行比较。因此,按照词典顺序排序,实际上应该是"2" > "15"

要使用我们自己的排序顺序,我们需要提供带两个参数的函数作为 arr.sort() 的参数。

arr 可以是由任何东西组成的数组。它可能包含数字或字符串或 html 元素或其他。我们对一组数据进行排序时,需要一个排序函数来确认如何比较这些元素。默认是按字符串排序的。

arr.sort(fn) 方法内置实现排序算法。我们不需要关心它是如何工作的(大多数情况下是优化过的快速排序算法)。它将自动遍历数组,使用提供的函数比较它的元素并对它们重新排序,我们所需要的只是提供用于比较的函数 fn

实际上,比较函数只需要返回一个正数表示更大,而负数表示更少。

let arr = [ 1, 2, 15 ];

arr.sort(function(a, b) { return a - b; }); //箭头函数可以使这句更简洁 arr.sort( (a, b) => a - b );

alert(arr);  // 1, 2, 15

reverse

arr.reverse 方法颠倒 arr 中元素的顺序。

let arr = [1, 2, 3, 4, 5];

arr.reverse();

alert( arr ); // 5,4,3,2,1

它也在返回后返回数组 arr

split 和 join

举一个现实生活的场景的例子,我们正在编写一个消息应用程序,并且该人员输入以逗号分隔的接收者列表:John,Pete,Mary。但对我们来说,数组比单个字符串更舒适。怎么做才能获得这个数组呢?

str.split(delim) 方法可以做到。它通过给定的分隔符 delim 将字符串分割成一个数组。

在下面的例子中,我们用逗号分隔:

let names = 'Bilbo, Gandalf, Nazgul';

let arr = names.split(', ');

for (let name of arr) {

  alert( `A message to ${name}.` ); // A message to Bilbo ...

}

split 方法有一个可选的第二个数字参数 — 对数组长度的限制。如果提供了,那么额外的元素将被忽略。但实际上它很少使用:

let arr = 'Bilbo, Gandalf, Nazgul, Saruman'.split(', ', 2);

alert(arr); // Bilbo, Gandalf

拆分为字母

  1. 调用空的参数 split(s) 会将字符串分成一个字母数组:

arr.join(str) 与 split 相反。它会在它们之间创建一串由 str 粘合的 arr 项。

例如:

let arr = ['Bilbo', 'Gandalf', 'Nazgul'];

let str = arr.join(';');

alert( str ); // Bilbo;Gandalf;Nazgul

reduce/reduceRight

当我们需要遍历一个数组时 — 我们可以使用 forEach

当我们需要迭代并返回每个元素的数据时 — 我们可以使用 map

arr.reduce 和 arr.reduceRight 和上面差不多,但有点复杂。它们用于根据数组计算单个值。

语法是:

let value = arr.reduce(function(previousValue, item, index, arr) {

  // ...

}, initial);

该函数应用于元素。从第二个参数开始你可能就会觉得很眼熟了:

  • item — 当前的数组元素。
  • index — 当前索引。
  • arr — 数组本身。

目前为止,这很像 forEach/map。但还有一个参数不同就是:

  • previousValue — 是前一个函数调用的结果,第一次调用是初始化。

我们写个例子试试。

这里我们得到一行数组的总和:

let arr = [1, 2, 3, 4, 5];

let result = arr.reduce((sum, current) => sum + current, 0);

alert(result); // 15

在这里,我们使用了 reduce 的最常见类型,它只使用 2 个参数。

让我们看看发生了什么的细节。

  1. 在第一次运行时,sum 是初始值(reduce 的最后一个参数),等于 0,current 是第一个数组元素,等于 1。所以结果是 1
  2. 在第二次运行时,sum = 1,我们添加第二个数组元素(2)并返回。
  3. 在第三次运行中,sum = 3,我们再添加一个元素,等等……

正如我们所看到的,先前调用的结果成为下一个调用的第一个参数。

我们也可以省略初始值:

let arr = [1, 2, 3, 4, 5];

// 删除初始值

let result = arr.reduce((sum, current) => sum + current);

alert( result ); // 15

结果是一样的。这是因为如果没有初始值,那么 reduce 将数组的第一个元素作为初始值,并从第二个元素开始迭代。

计算表与上面相同,减去第一行

但是这种使用需要非常小心。如果数组为空,那么在没有初始值的情况下调用 reduce 会导致错误。

let arr = [];

// Error: Reduce of empty array with no initial value

// 如果初始值存在,reduce 将返回空 arr。

arr.reduce((sum, current) => sum + current);

arr.reduceRight 也一样,但是遍历是从右到左。

迭代:forEach

arr.forEach 方法允许为数组的每个元素运行一个函数。

语法:

arr.forEach(function(item, index, array) {

  // ... do something with item

});

例如:

["Bilbo", "Gandalf", "Nazgul"].forEach((item, index, array) => {

  alert(`${item} is at index ${index} in ${array}`);

});

大多数方法支持 “thisArg”

几乎所有调用函数的数组方法 – 比如 findfiltermap,与带有 sort 的不同,他们接受一个可选的附加参数 thisArg

该参数在上面的部分没有解释,因为它很少使用。但为了完整性,我们还需要解释下。

以下是这些方法的完整语法:

arr.find(func, thisArg);

arr.filter(func, thisArg);

arr.map(func, thisArg);

// ...

// thisArg 是可选的最后一个参数

thisArg 参数的值在 func 中变为 this

例如,在这里我们使用一个对象方法作为过滤器,thisArg 派上用场:

let user = {

  age: 18,

  younger(otherUser) {

    return otherUser.age < this.age;

  }

};

let users = [

  {age: 12},

  {age: 16},

  {age: 32}

];

// 找到比 user 小的所有 users

let youngerUsers = users.filter(user.younger, user);

alert(youngerUsers.length); // 2

在上面我们使用 user.younger 作为过滤器,并提供 user 作为它的上下文。如果我们没有提供上下文,users.filter(user.younger) 会调用user.younger 作为一个独立的函数,这时 this=undefined

总结

数组方法备忘录:

  • 添加/删除元素:

    • push(...items) — 从结尾添加元素,
    • pop() — 从结尾提取元素,
    • shift() — 从开头提取元素,
    • unshift(...items) — 从开头添加元素,
    • splice(pos, deleteCount, ...items) — 从 index 开始:删除 deleteCount 元素并在当前位置插入元素。
    • slice(start, end) — 它从所有元素的开始索引 "start" 复制到 "end" (不包括 "end") 返回一个新的数组。
    • concat(...items) — 返回一个新数组:复制当前数组的所有成员并向其中添加 items。如果有任何items 是一个数组,那么就取其元素。
  • 查询元素:

    • indexOf/lastIndexOf(item, pos) — 从 pos 找到 item,则返回索引否则返回 -1
    • includes(value) — 如果数组有 value,则返回 true,否则返回 false
    • find/filter(func) — 通过函数过滤元素,返回 true 条件的符合 find 函数的第一个值或符合 filter 函数的全部值。
    • findIndex 和 find 类似,但返回索引而不是值。
  • 转换数组:

    • map(func) — 从每个元素调用 func 的结果创建一个新数组。
    • sort(func) — 将数组倒序排列,然后返回。
    • reverse() — 在原地颠倒数组,然后返回它。
    • split/join — 将字符串转换为数组并返回。
    • reduce(func, initial) — 通过为每个元素调用 func 计算数组上的单个值并在调用之间传递中间结果。
  • 迭代元素:

    • forEach(func) — 为每个元素调用 func,不返回任何东西。
  • 其他:  – Array.isArray(arr) 检查 arr 是否是一个数组。

请注意,sortreverse 和 splice 方法修改数组本身。

这些方法是最常用的方法,它们覆盖 99% 的用例。但是还有其他几个:

  • arr.some(fn)/arr.every(fn) 检查数组。

    在类似于 map 的数组的每个元素上调用函数 fn。如果任何/所有结果为 true,则返回 true,否则返回 false

  • arr.fill(value, start, end) — 从 start 到 end 用 value 重复填充数组。

  • arr.copyWithin(target, start, end) — copies its elements from position start till position end into itself, at position target (overwrites existing).将其元素从 start 到 end 在 target 位置复制到 本身(覆盖现有)。

解构赋值

解构赋值是一种特殊的语法,它让我们可以将数组或对象进行“拆包”,存放到一系列的变量中,因为有时候使用变量更加方便。解构操作在那些具有很多参数和默认参数值的函数中也很奏效,

数组解构

以下是将数组解构到变量中的一个例子:

// 有一个存放了名字和姓氏的数组

let arr = ["Ilya", "Kantor"]

// 解构赋值

let [firstName, surname] = arr;

alert(firstName); // Ilya

alert(surname);  // Kantor

现在我们就可以针对这些变量进行操作,而不是针对原来的数组元素。

当与 split 函数(或其他返回值是数组的函数)结合使用时,看起来就更优雅了:

let [firstName, surname] = "Ilya Kantor".split(' ');
这种语法叫做“解构赋值”,因为它通过将结构中的各元素复制到变量中来达到“解构”的目的。但数组本身是没有被修改的。

也就是以下语句的更精简写法而已

// let [firstName, surname] = arr;

let firstName = arr[0];

let surname = arr[1];

数组中不想要的元素也可以通过添加额外的逗号来把它丢弃:

// 不需要第一个和第二个元素

let [, , title] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];

alert( title ); // Consul

在以上的代码中,数组的第一个和第二个元素被跳过,第三个元素被赋值给了 title 变量,剩下的元素也被跳过了。

我们可以使用 .entries() 方法和解构语法来遍历一个对象的键-值对:

let user = {

  name: "John",

  age: 30

};

// 循环遍历键-值对

for (let [key, value] of Object.entries(user)) {

  alert(`${key}:${value}`); // name:John, then age:30

}

对于 map 对象也类似:

let user = new Map();

user.set("name", "John");

user.set("age", "30");

for (let [key, value] of user.entries()) {

  alert(`${key}:${value}`); // name:John, then age:30

}

剩余的 ‘…’

如果我们不仅要获得第一个值,还要将后续的所有元素也收集起来——我们可以使用三个点 "..." 加一个参数来接收“剩余的”元素:

let [name1, name2, ...rest] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];

alert(name1); // Julius

alert(name2); // Caesar

alert(rest[0]); // Consul

alert(rest[1]); // of the Roman Republic

alert(rest.length); // 2

rest 变量的值就是数组中剩下的元素组成的数组。不一定要使用变量名 rest,我们也可以使用其他的变量名,只要确保它前面有三个点,并且在解构赋值的最后一个参数位置上就行了。

默认值

如果赋值语句中变量的数量多于数组中实际元素的数量,赋值不会报错。未赋值的变量被当作 undifined

let [firstName, surname] = [];

alert(firstName); // undefined

所以如果我们想要提供一个“默认值”给未赋值的变量,我们可以使用 = 来提供:

// 默认值

let [name = "Guest", surname = "Anonymous"] = ["Julius"];

alert(name);    // Julius (来自数组的值)

alert(surname); // Anonymous (默认值被使用了)

默认值可以是更加复杂的表达式甚至可以是函数调用,这些表达式或函数只会在这个变量未被赋值的时候才会被计算。

对象解构

解构赋值同样适用于对象。

基本的语法是:let {var1, var2} = {var1:…, var2…}

在等号右侧有一个已经存在的对象,我们想把它拆开到变量中。等号左侧包含了对象相应属性的一个“模式”。

就像数组或函数参数一样,默认值可以是表达式甚至是函数调用。只会在这个变量未被赋值的时候才会被计算/调用。

以下的代码提示输入宽度 width,但不会提示输入标题 title。

let options = {

  title: "Menu"

};

let {width = prompt("width?"), title = prompt("title?")} = options;

alert(title);  // Menu

alert(width);  // 你输入的宽度值

如果我们想把一个属性赋值给不同名字的变量,比如把 options.width 属性赋值给变量 w,那可以使用冒号来指定:

let options = {

  title: "Menu"

};

let {width: w = 100, height: h = 200, title} = options;

alert(title);  // Menu

alert(w);      // 100

alert(h);      // 200

剩余操作符

如果对象拥有的属性数量比我们提供的变量数量还多怎么办?我们可以只取其中的某一些属性然后把“剩余的”赋值到其他地方吗?

关于剩余操作符(即三个点)的文档几乎已经要被列为标准了,但大部分的浏览器还尚未支持。

let options = {

  title: "Menu",

  height: 200,

  width: 100

};

let {title, ...rest} = options;

// now title="Menu", rest={height: 200, width: 100}

alert(rest.height);  // 200

alert(rest.width);   // 100

注:

let title, width, height;

{title, width, height} = {title: "Menu", width: 200, height: 100}; // 这一行发生错误

问题在于 JavaScript 把主代码流(即不在其他表达式中)的 {...} 当做一个代码块,这样的代码块可以被用来组织语句,如下:

{

  // 一个代码块

  let message = "Hello";

  // ...

  alert( message );

}

为了告诉 JavaScript 这不是一个代码块,我们可以把整个赋值表达式用括号 (...) 包起来:

let title, width, height;

// 现在就正确了

({title, width, height} = {title: "Menu", width: 200, height: 100});

alert( title ); // Menu

嵌套解构

如果一个对象或数组包含了其他的对象和数组,我们可以在等号左侧使用更复杂的模式来抽取深层的数据。

在以下代码中 options 的属性 size 是另一个对象,属性 items 是另一个数组。赋值语句中等号左侧的模式拥有相同的结构:

let options = {

  size: {

    width: 100,

    height: 200

  },

  items: ["Cake", "Donut"],

  extra: true    // 一些不会被解构的额外属性

};

// 为了清晰起见,解构赋值语句被写成多行

let {

  size: { // 把 size 赋值到这里

    width,

    height

  },

  items: [item1, item2], // 把 items 赋值到这里

  title = "Menu" // 在对象中不存在的属性(会使用默认值)

} = options;

alert(title);  // Menu

alert(width);  // 100

alert(height); // 200

alert(item1);  // Cake

alert(item2);  // Donut

最终,我们得到了 widthheightitem1item2 和具有默认值的 title 变量。

有一个拥有很多属性的复杂对象,我们只想要抽取我们所需要的其中某些属性。这在解构赋值语句中是很常见的。

甚至还可能是这样的情况:

// 将 size 作为一个整体取出赋值给一个变量,忽略剩下的所有

let { size } = options;

智能函数参数

有时候一个函数可能有很多参数,大部分的参数是可选的

现实情况下的问题就是你怎么记得住这么多参数的顺序,通常集成开发环境工具(IDE)会尽力帮助我们,特别是当代码有良好的文档注释的时候,但… 另一个问题就是当大部分的参数采用默认值就好的情况下,怎么调用这个函数。

难道像这样?

showMenu("My Menu", undefined, undefined, ["Item1", "Item2"])

这太难看了。而且当我们处理更多参数的时候可读性还会变得更差。

解构赋值语法前来支援!

我们可以把所有参数当作一个对象来传递,然后函数马上把这个对象解构成多个变量:

let options = {

  title: "My menu",

  items: ["Item1", "Item2"]

};

// ...然后函数马上把对象展开成变量

function showMenu({

  title = "Untitled",

  width: w = 100,  // width 赋值给 w

  height: h = 200, // height 赋值给 h

  items: [item1, item2] // items 第一个元素赋值给 item1, 第二个元素赋值给 item2

}) {

  alert( `${title} ${w} ${h}` ); // My Menu 100 200

  alert( item1 ); // Item1

  alert( item2 ); // Item2

}

showMenu(options);

请注意,这种解构假定了调用 showMenu() 函数时传递了一个参数,如果我们想让所有的参数都使用默认值,那我们应该传递一个空的对象:

showMenu({});

showMenu(); // 这样会导致错误

我们可以通过指定空对象 {} 为整个函数参数的默认值:

// 清晰起见,精简了部分参数

function showMenu({ title = "Menu", width = 100, height = 200 } = {}) {

  alert( `${title} ${width} ${height}` );

}

showMenu(); // Menu 100 200

在以上的代码中,整个参数对象默认就是 {},因此总会有对象可以用来解构。

总结

  1. 解构赋值允许将对象或数组立即映射到多个变量上。

  2. 解构对象的语法:

  3. let {prop : varName = default, ...} = object

  1. 这表示属性 prop 会被赋值给变量 varName,如果没有这个属性的话,就会使用 default 的值。

  2. 解构数组的语法:

  3. let [item1 = default, item2, ...rest] = array

  1. 数组的第一个元素赋值给 item1,第二个元素赋值给 item2,剩下的所有组成另一个数组 rest

  2. 更多复杂的案例情况下,等号左侧必须和等号右侧有相同的结构。




完成了登录页

用Element UI完成了后台管理系统头部,侧边栏组件及子路由跳转框架

设置登录时,出了点问题,发现不像我想象中那么简单

一、跨域

本想不设置nginx 直接使用axios内置的本地跨域配置,即

Step1:配置BaseUrl

首先在main.js中,配置下我们访问的Url前缀:

import Axios from "axios"

Vue.prototype.$axios = Axios

Axios.defaults.baseURL = "http://dev.admin.carrots.ptteng.com"     //配置默认发送请求到http://url,可改或者加端口号,这样每次发送请求都会带一个http//url的前缀。

2.接着配置config/index.js,找到proxyTable,内部添加如下代码

 proxyTable: {

        '/carrots-admin-ajax': {

            target: 'http://dev.admin.carrots.ptteng.com',//设置调用的接口域名和端口号(默认端口号80)

            changeOrigin: true, //允许跨域

            pathRewrite: {'^/carrots-admin-ajax': '' }

              //这里理解成用‘/carrots-admin-ajax’代替target里面的地址,

        }

    },

因为我们给url加上了前缀 /carrots-admin-ajax,我们访问 http://localhost:80/a/login 就当于访问了:http://localhost:80/carrots-admin-ajax/a/login。(假设本地访问端口号为80)

又因为在 index.js 中的 proxyTable 中拦截了 /carrots-admin-ajax ,并把  /carrots-admin-ajax 及其前面的所有替换成了 target 中的内容,因此实际访问 Url 是http://dev.admin.carrots.ptteng.com/a/login

完成配置后,发送请求,后台提示

Access to XMLHttpRequest at 'http://dev.admin.carrots.ptteng.com/a/login' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

即已拦截跨源请求:同源策略禁止读取位于XXX的远程资源。(原因:CORS 头缺少 ' Access-Control-Allow-Origin ')

然后才了解到

这叫跨域资源共享CORS,这么做需要后台添加请求头,才可以进行跨域访问,但一般不建议这样进行跨域,很容易遭XSS(虽然不懂是什么)  。

回到反向代理服务器






返回列表 返回列表
评论

    分享到