options请求和axios跨域及cookie传递问题

options请求: 检测请求的接口的信息(服务器接受的方法、跨域中测实际请求是否可以被服务器所接受)

什么情况下会发送options预检请求

浏览器将CORS请求分为两类:简单请求、非简单请求

  • 简单请求(不会发送预检请求)与非简单请求(请求之前发送options)的区别

    1. 请求方法:get、post、head

    2. http请求头信息:
      accept
      accept-language、
      content-language、
      content-type、
      last-event-id

    3. content-type:
      application/x-www-form-urlencoded、
      multipart/form-data、
      text/plain

      同时满足以上两个条件时,才是简单请求,否则为非简单请求

axios跨域并获取cookie

axios详细配置文档

核心:
1.axios全局配置中添加 withCredentials: true 跨域并保存cookie
2.在请求拦截器内设置 content-type:application/json

不是很理解第二点 加了之后不是把所有请求都变成非简单请求了吗
但是不加的话就报错了

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
import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import store from '@/store'

// create an axios instance
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest' // ! 可能是非必须
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
withCredentials: true, // send cookies when cross-domain requests
timeout: 5000 // request timeout
})

// request interceptor 请求拦截
service.interceptors.request.use(
config => {
config.headers = {
'Content-Type': 'application/json' // 注意:设置很关键
}
return config
},
error => {
// do something with request error
console.log(error) // for debug
return Promise.reject(error)
}
)

后端相应的配置:

1
2
3
response.setHeader('Access-Control-Allow-Credentials',true); // 必须加上
response.setHeader('Access-Control-Allow-Origin','http://localhost:9528'); //不能写成 * ,一定要指定地址
response.setHeader('Set-Cookie','token=cowshield'); //传递的数据

问题描述

按理说应该是可以了
但是后台说因为有options预检请求
所以请求会有两次 但是他能获取的是options那次 后面那个请求获取不到
但是options请求又没有能保存传递的数据 导致一些问题

解决方案

我觉得不严谨 但是有效
大概是 写了一个拦截器 对于options请求直接返回true 就可以进入下一个请求了

参考资料

http://www.ruanyifeng.com/blog/2016/04/cors.html
https://blog.csdn.net/qq_25551573/article/details/80690583

CSS相关

想闲暇的时候学点CSS动画的实现方法,
希望能持续更新 见证自己的成长 ,实现的效果也能越来越复杂好看!

波纹效果

充电动画

核心原理 filter的两个属性 blur(高斯模糊)px、contrast(对比度)%、hue-rotate(色相旋转) deg
有个疑问 就是w3c里面查的contrast的单位应该是 % 这个例子里面如果给它加了单位就实现不了了 就直接改变整体的对比度
blur(px): 默认为0,数值越大越模糊
contrast(%): 0%全黑,100%图像无变化。默认为1,超过100%图像会比原来亮
hue-rotate(deg):0deg图像无变化。默认是0deg。该值虽然没有最大值,超过360deg的值相当于又绕一圈。
Tips:
背景一般选择黑色或者白色,不然会受到contrast和hue-rotate的影响
在实现渐变效果的时候 filter: contrast(20) hue-rotate(0deg); 顺序不能变

内部设置高斯模糊 外层盒子不设置 对比度
外层盒子设置 对比度
(好像是在视觉上抵消内部盒子的高斯模糊)
内部盒子相遇时
内部盒子运动+渐变颜色
(filter: contrast(20) hue-rotate(0deg); 顺序不能变!!!)

成品效果:

【第六章】代理模式

定义: 为一个对象提供一个代用品或占位符,以控制对它的访问
关键:当客户不方便直接访问一个对象或者不满足需求的时候,提供一个替身对象来控制对这个对象的访问,客户实际上访问的是替身对象。替身对象对请求作出一些处理之后,再把请求转交给本体对象

不使用代理模式: 客户 ——> 本体
使用代理模式: 客户 ——> 代理 ——> 本体

一个解释代理模式的例子

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/*
M 直接送花给 A
*/
var Flower = function(){}
var M = {
sendFlower: function(target){
var flower = new Flower()
target.receiveFlower(flower)
}
}
var A = {
receiveFlower: function(flower){
console.log('收到花'+flower)
}
}
M.sendFlower(A)

/*
M 通过 B 送花给 A
*/
var Flower = function(){}
var M = {
sendFlower: function(target){
var flower = new Flower()
target.receiveFlower(flower)
}
}
var B = {
receiveFlower: function(flower){
A.receiveFlower(flower)
}
}
var A = {
receiveFlower: function(flower){
console.log('收到花'+flower)
}
}
M.sendFlower(B)

/*
现在我们改变故事的背景设定,假设当 A 在心情好的时候收到花,小明表白成功的几率有60%,而当 A 在心情差的时候收到花,小明表白的成功率无限趋近于 0。
小明跟 A 刚刚认识两天,还无法辨别 A 什么时候心情好。如果不合时宜地把花送给 A,花被直接扔掉的可能性很大
但是 A 的朋友 B 却很了解 A,所以小明只管把花交给 B,B 会监听 A 的心情变化,然后选择 A 心情好的时候把花转交给 A,
*/
var Flower = function () {}
var M = {
sendFlower: function (target) {
var flower = new Flower()
target.receiveFlower(flower)
}
}
var B = {
receiveFlower: function (flow) {
A.hasGoodMood(function(){
A.receiveFlower(flow)
})

}
}
var A = {
receiveFlower: function (flow) {
console.log('get flower' + flow)
},
hasGoodMood: function(fn){
setTimeout(() => {
fn()
}, 1000);
}
}

M.sendFlower(B)

保护代理和虚拟代理

代理B可以帮助A过滤掉一些请求的叫做保护代理 (通过B来控制对A的访问)
代理 B 会选择在 A 心情好时再执行 new Flower叫作虚拟代理。 (虚拟代理把一些开销很大的对象,延迟到真正需要它的时候才去创建)

1
2
3
4
5
6
7
8
9
var B = {
receiveFlower: function(flower){
A.listenGoodMood(function(){ //监听A的好心情
var flower = new Flower() //延迟创建flower对象
A.receiveFlower(flower)
})
}
}

虚拟代理实现图片预加载

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

var oImg = (function(){
var imgNode = document.createElement('img')
document.body.appendChild(imgNode)
return {
setSrc : function(src){
imgNode.src = src
}
}
})()

//代理对象
var proxyImage = (function(){
var img = new Image() //获取的img对象
img.onload = function(){
oImg.setSrc(this.src) //img.src
}
return {
setSrc: function(src){
oImg.setSrc('./loading.gif')
img.src = src
}
}
})()
proxyImage.setSrc('http://....jpg')

代理的意义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//不使用代理的方式
var oImg = (function(){
var imgNode = document.createElement('img')
document.body.appendChild(imgNode)
var img = new Image
img.onload = function(){
imgNode.src = img.src
}
return {
setSrc : function(src){
imgNode.src = './timg.jpg'
img.src = src
}
}
})()
oImg.setSrc('https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=629448732,2965351611&fm=15&gp=0.jpg')

单一职责原则:为了减少代码的耦合性,方便维护和修改

代理和本体接口的一致性

调用时的代理接口和本地接口名称一致 也通过直接返回函数

优点:

  1. 用户可以放心地请求代理,他只关心是否能得到想要的结构
  2. 在任何使用本体的地方都可以替换成使用代理
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    var myImage = (function(){
    var imgNode = document.createElement('img')
    document.appendChild(imgNode)
    return function(src){
    imgNode.src = src
    }
    })()

    var proxyImage = (function(){
    var img = new Image
    img.onload = function(){
    myImage( this.src )
    }
    return function(src){
    myImage('./timg.jpg')
    img.src = src
    }
    })()
    proxyImage('https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=629448732,2965351611&fm=15&gp=0.jpg')

【第五章】策略模式

策略模式

比如要实现某一个功能有多种方案可以选择。比如压缩文件的程序,既可以选择zip算法,也可以选择gzip算法

定义: 定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换

  • 策略模式的优缺点
    优点:

      1. 策略模式利用组合、委托和多态等技术和思想,可以有效地避免多重条件选择语句。
      2. 策略模式提供了对开放—封闭原则的完美支持,将算法封装在独立的 strategy 中,使得它们易于切换,易于理解,易于扩展。
      3. 策略模式中的算法也可以复用在系统的其他地方,从而避免许多重复的复制粘贴工作。
      4. 在策略模式中利用组合和委托来让 Context 拥有执行算法的能力,这也是继承的一种更轻便的替代方案。

    缺点:

      1. 使用策略模式会在程序中增加许多策略类或者策略对象,但实际上这比把它们负责的逻辑堆砌在 Context 中要好
      2. 要使用策略模式,必须了解所有的 strategy,必须了解各个 strategy 之间的不同点,这样才能选择一个合适的 strategy。
    • 使用策略模式计算奖金

      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
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      /*
      绩效为 S 的人年终奖有 4 倍工资,绩效为 A 的人年终奖有 3 倍工资,而绩效为 B 的人年终奖是 2 倍工资。假设财务部要求我们提供一段代码,来方便他们计算员工的年终奖
      */

      //最初代码实现
      var calculateBonus = function(performanceLevel,salary){
      if(performanceLevel === 'S')return salary*4
      if(performanceLevel === 'A')return salary*3
      if(performanceLevel === 'B')return salary*2
      }
      caculateBonus('B',2000) //输出 4000
      /*

      缺点:
      1. calculateBonus 函数比较庞大,包含了很多 if-else 语句,这些语句需要覆盖所有的逻辑分支。
      2. calculateBonus 函数缺乏弹性,如果增加了一种新的绩效等级 C,或者想把绩效 S 的奖金系数改为 5,那我们必须深入 calculateBonus 函数的内部实现,这是违反开放-封闭原则的。
      3. 算法的复用性差,如果在程序的其他地方需要重用这些计算奖金的算法呢?我们的选择只有复制和粘贴。

      */

      //使用策略模式重构代码
      //目的: 算法的使用与算法的实现分离
      var perfromanceS = function(){}
      perfromanceS.prototype.calculate = function( salary ){
      return salary*4
      }

      var perfromanceA = function(){}
      perfromanceA.prototype.calculate = function( salary ){
      return salary*3
      }

      var perfromanceB = function(){}
      perfromanceB.prototype.calculate = function( salary ){
      return salary*2
      }

      //奖金类
      var Bonus = function(){
      this.salary = null //原始工资
      this.strategy = null //绩效等级对应的策略对象
      }
      Bonus.prototype.setSalary = function(salary){
      this.salary = salary //设置员工的原始工资
      }
      Bonus.prototype.setStrategy = function(strategy){
      this.strategy = strategy
      }
      Bonus.prototype.getBonus = function(){
      return this.strategy.calculate(this.salary) //把计算奖金的操作委托给对应的策略对象
      }
      var bonus = new Bonus();
      bonus.setSalary( 10000 );
      bonus.setStrategy( new performanceS() ); // 设置策略对象
      console.log( bonus.getBonus() ); // 输出:40000
      bonus.setStrategy( new performanceA() ); // 设置策略对象
      console.log( bonus.getBonus() ); // 输出:30000
    • JavaScript版本的策略模式

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      /*
      我们让 strategy 对象从各个策略类中创建而来,这是模拟一些传统面向对象语言的实现。实际上在 JavaScript 语言中,函数也是对象,所以更简单和直接的做法是把 strategy直接定义为函数:
      */
      //策略对象
      var strategies = {
      "S": function( salary ){
      return salary*4
      },
      "A": function( salary ){
      return salary*3
      },
      "B": function( salary ){
      return salary*2
      }
      }
      //操作类
      var calculateBonus = function(level,salary){
      return strategies[level](salary)
      }
      calculateBonus('S',200) //800

【第四章】单例模式

定义: 保证一个类仅有一个实例,并提供一个访问它的全局访问点。在 JavaScript 开发中,单例模式的用途同样非常广泛。试想一下,当我们单击登录按钮的时候,页面中会出现一个登录浮窗,而这个登录浮窗是唯一的,无论单击多少次登录按钮,这个浮窗都只会被创建一次,那么这个登录浮窗就适合用单例模式来创建。

核心:确保只有一个实例,并提供全局访问

  1. 实现单例模式

    用一个变量来标志当前是否已经为某个类创建过对象,如果是,则在下一次获取该类的实例时,直接返回之前创建的对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    var Singleton = function(name){
    this.name = name
    this.instance = null //存放已经生成的实例对象
    }
    Singleton.prototype.getName = function(){
    console.log(this.name)
    }
    Singleton.getInstance = function(name){
    if(!this.instance){
    this.instance = new Singleton(name)
    }
    return this.instance
    }

    var a = Singleton.getInstance('doreen')
    var b = Singleton.getInstance('sherry')
    a.getName() //doreen
    b.getName() //doreen
    console.log(a === b) //true

  2. 透明的单例模式

    使用的时候要和普通类一样通过new去创建实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // 利用闭包的方式 让变量存在局部
    var CreateDiv = (function(){
    var instance
    var CreateDiv = function(html){
    if(instance){
    return instance
    }
    this.html = html
    this.init()
    return instance = this
    }
    CreateDiv.prototype.init = function(){
    var div = document.createElement('div')
    div.innerHTML = this.html
    document.body.appendChild(div)
    }
    return CreateDiv;
    })()
    var a = new CreateDiv('doreen')
    var b = new CreateDiv('sherry')
    console.log(a === b) // true

  3. 用代理实现单例模式

    CreateDiv变成普通类,它跟proxySingletonCreateDiv组合起来可以达到单例模式的效果

    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
    var CreateDiv = function(html){
    this.html = html
    this.init()
    }
    CreateDiv.prototype.init = function(){
    var div = document.createElement('div')
    div.innerHTML = this.html
    document.body.appendChild(div)
    }

    //代理类 proxySingletonCreateDiv
    var proxySingletonCreateDiv = (function(){
    var instance
    return function(html){
    if(!instance){
    instance = new CreateDiv(html)
    }
    return instance
    }
    })()

    var a = new proxySingletonCreateDiv('doreen')
    var b = new proxySingletonCreateDiv('sherry')
    console.log(a===b) // true

  4. 惰性单例

    是在需要的时候才创建对象实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    /* 创建一个登录框  需要登录的时候才动态创建登录框*/
    var createLoginLayer = (function () {
    var div
    return function () {
    if (!div) {
    div = document.createElement('div')
    div.innerHTML = '登录框'
    div.style.display = 'none'
    document.body.appendChild(div)
    }
    return div
    }
    })()

    var loginLayer = createLoginLayer()
  5. 通用的惰性单例

    惰性单例存在的问题

    1. 违反单一职责原则,创建对象和管理单例的逻辑都放在createLoginLayer对象内部
    2. 如果下次需要创建页面中唯一的iframe,或者script标签,用来跨域请求数据,就必须如法炮制,把createLoginLayer函数几乎照抄一遍
      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
      29
      var createIframe = (function(){
      var iframe
      return function(){
      if(!iframe){
      iframe = document.createElement('iframe')
      iframe.style.display = 'none'
      document.body.appendChild(iframe)
      }
      return iframe
      }
      })()

      // 把代码抽离出来
      // 公用的部分 单例逻辑
      var getSingle = function(fn){
      var result
      return function(){
      return result || (result = fn.apply(this,arguments))
      }
      }
      // 创建对象的方法
      var createLoginLayer = function(){
      var div = document.createElement('div')
      div.innerHTML = '登录框'
      div.style.display = 'none'
      document.body.appendChil(div)
      return div
      }
      var createSingleLoginLayer = getSingle(createLoginLayer)

【第三章】闭包和高阶函数

闭包作用

封装变量

把不需要暴露在全局的变量封装成 “私有变量”

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
//计算乘积一般方法
var mult = function(){
var a = 1
for(var i=0;l=arguments.length;i<l;i++){
a *= arguments[i]
}
return a
}
/*
对于相同的参数来说,每次进行计算是一种浪费
所以可以加入缓存机制来提高这个函数的性能
*/
var cache = {}
var mult = function () {
var args = Array.prototype.join.call(arguments, ',')
//args = '1,2,3' 类数组使用join方法
if (cache[args]) {
return cache[args]+'[]'
}
var a = 1
var l = arguments.length;
for (var i = 0; i < l; i++) {
a *= arguments[i]
}
return cache[args] = a
// cache = { '1,2,3': 6 ,...}
}
console.log(mult(1,2,3)) // 6
console.log(mult(1,2,3)) // 6[] 不进行再次循环 直接取cache内存的值
console.log(mult(1,2,5)) // 10
console.log(cache) // { '1,2,3': 6 ,'1,2,5': 10}
/*
虽然 cache 只在mult函数中使用
但是 被放在了全局变量中 不能被释放
实现在mult中
通过闭包的方式实现 */
var mult = (function(){
var cache = {}
return function(){
var args = Array.prototype.join.call(arguments, ',')
if (cache[args]) {
return cache[args]+'[]'
}
var a = 1
var l = arguments.length;
for (var i = 0; i < l; i++) {
a *= arguments[i]
}
return cache[args] = a
}
})()
//提炼函数
var mult = (function(){
var cache = {}
var calculate = function(){
var a = 1
for(var i=0,l=arguments.length;i<l;i++){
a *= arguments[i]
}
return a
}

return function(){
var args = Array.prototype.join.call(arguments,',')
if( args in cache ){
return cache[args]
}
return cache[args] = calculate.apply(null,arguments)
//用apply主要是因为arguments是一个数组 更方便传参 不需要处理类型
//es6也可以直接用 calculate(...arguments)
}
})()

延长局部变量的寿命

变量一直处于被引用状态 所以不会释放缓存?

高阶函数

需要满足的条件

  1. 函数可以作为参数被传递
  2. 函数可以作为返回值输出

函数作为参数传递

  1. 回调函数

    回调函数的应用不仅只在异步请求中,当一个函数不适合执行一些请求时,我们也可以把这些请求封装成一个函数,并把它作为参数传递给另外一个函数,“委托”给另外一个函数来执行。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var appendDiv = function (callback) {
    for (var i = 0; i < 100; i++) {
    var div = document.createElement('div');
    div.innerHTML = i;
    document.body.appendChild(div);
    if (typeof callback === 'function') {
    callback(div);
    }
    }
    };
    appendDiv(function (node) {
    node.style.display = 'none'
    })
    1. Array.prototype.sort
      Array.prototype.sort 接受一个函数当作参数,这个函数里面封装了数组元素的排序规则。从Array.prototype.sort 的使用可以看到,我们的目的是对数组进行排序,这是不变的部分;而使用什么规则去排序,则是可变的部分。把可变的部分封装在函数参数里,动态传入Array.prototype.sort,使 Array.prototype.sort 方法成为了一个非常灵活的方法,代码如下
      1
      2
      3
      4
      5
      6
      7
      8
      9
      //从小到大排列
      [ 1, 4, 3 ].sort( function( a, b ){
      return a - b;
      }) // [1,3,4]

      //从大到小排序
      [ 1, 4, 3 ].sort( function( a, b ){
      return b - a;
      }) // [4,3,1]

    函数作为返回值输出

  2. 判断数据的类型

    通过 Object.prototype.toString.call(obj) 判断数据类型(最好的方法)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var isType = function(type){
    return function(obj){
    return Object.prototype.toString.call(obj) == '[object '+type+']'
    }
    }

    isType('String')('a') //true
    isType('Array')([1,2]]) //true

【第二章】this、call和apply

this

指向一个对象。具体指向哪一个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境。

执行上下文:

JavaScript引擎在JavaScript代码正式执行之前会先创建执行环境,在执行环境中作预处理工作

  • 工作内容:
    1.创建空对象
    2.该空对象用于收集变量、函数,函数的参数
    3.创建作用域链
    4.确定this的指向

  • 执行上下文栈:
    1.在全局代码执行之前,JavaScript引擎就会创建一个栈来存储管理所有的执行上下文对象
    2.在全局执行上下文确定后,将其添加到栈中(压栈)
    3.在函数执行上下文创建后,将其添加到栈中(压栈)
    4.在当前函数执行完后,将栈顶的对象移除(出栈)
    5.当所有的代码执行完后,栈中只剩下window

  • this指向
    1.作为对象的方法调用 指向对象
    2.作为普通函数调用 指向全局对象(window)
    3.构造函数调用 指向实例对象
    4.Function.prototype.call或Function.prototype.apply调用 指向第一个参数对象

    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
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    //1.作为对象的方法调用
    var obj = {
    a : 1,
    getA : function(){
    console.log( this === obj ) //true
    console.log( this.a ) // 1
    }
    }

    //2.作为普通函数调用
    window.name = 'globalName'
    var obj = {
    name : 'localName',
    getName : function(){
    console.log(this.name)
    }
    }
    var getName = obj.getName
    getName() //globalName
    obj.getName() //localName

    //3.构造函数调用
    var Obj = function(){
    this.name = 'doreen'
    this.age = 11
    }
    var obj = new Obj()
    obj.name = 'sherry'
    console.log(obj) //sherry

    //4.Function.prototype.call或Function.prototype.apply调用
    var obj = {
    name: 'doreen',
    getName: function(){
    return this.name
    }
    }

    var obj2 = {
    name: 'sherry'
    }
    console.log(obj.getName()) //doreen
    console.log(obj.getName.call(obj2)) //sherry

call和apply

  • call和apply的区别

    call多个参数 .call(对象,…)
    apply是两个参数 .apply(对象,数组)

1
2
3
4
5
6
var func = function(a,b,c){
console.log([a,b,c])
}

func.call(null,1,2,3) //[1,2,3]
func.apply(null,[1,2,3]) //[1,2,3]
  • call和apply的用途

    1. 改变this指向

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      var obj1 = {
      name: 'doreen'
      }
      var obj2 = {
      name: 'sherry'
      }
      var window.name = 'winow'
      var getName = function(){
      console.log(this.name)
      }

      getName() //window
      getName.call(obj1) //doreen
      getName.call(obj2) //sherry
    2. Function.prototype.bind

      1
      2
      3
      4
      5
      6
      7
      // 实现方法
      Function.prototype.bind = function(context){
      var obj = this
      return function(){
      return obj.apply(context,arguments)
      }
      }

      延迟执行,call和apply是改变this之后直接执行函数,而bind是返回一个函数 需要在调用才能执行

    3. 借用其他对象的方法

    • 场景一 “ 借用构造函数 ” 实现继承

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      var A = function(name){
      this.name = name
      }
      var B = function(){
      A.apply(this,arguments)
      }
      B.prototype.getName = function(){
      console.log(this.name)
      }
      var b = new B('doreen')
      b.getName() //doreen
    • 场景二 处理 “ 类数组对象 ”

      函数的参数列表 arguments 是一个类数组对象,虽然它也有“下标”,但它并非真正的数组,所以也不能像数组一样,进行排序操作或者往集合里添加一个新的元素。这种情况下,我们常常会借用 Array.prototype 对象上的方法。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      (function(){
      Array.prototype.push.call(arguments,3)
      console.log(arguments) //[1,2,3]
      })(1,2)

      /*数组的特性
      1.对象本身要可以存取属性
      2.对象的length属性可读写
      */

【第一章】面向对象的JavaScript

动态类型语言和鸭子类型

编程语言按照数据类型 可以分为 静态类型语言和动态类型语言

        静态类型语言:声明时确定变量类型
            优:
            1. 在编译时就能发现类型不匹配的错误,提前避免程序在运行期间有可能发生的一些错误。[提高可靠性]
            2. 如果在程序中明确地规定了数据类型,编译器还可以针对这些信息对程序进行一些优化工作,提高程序执行速度。
            缺:
            1. 具有强迫性 不够灵活
            2. 类型的声明会增加代码量 分散业务逻辑这个重点
        动态类型语言:赋值时确定变量类型(JavaScript)
            优:
            1. 简化代码量 侧重点更能集中在业务逻辑上面
            2. 代码灵活性增强
            缺:
            1. 无法保证变量的类型 [可靠性低]

动态语言和鸭子类型的关系

鸭子类型概念:如果它走起路来像鸭子,叫起来也是鸭子,那么它就是鸭子(关注的是has a xx 而不是 is a xx)

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
/**
我的理解:
动态语言无法保证变量的类型
那么只要他所赋值的变量有某种类型的特性 那么它就是某一类型
*/
//书中的例子
var duck = { //鸭子
voice: function(){
console.log('嘎嘎嘎')
}
}
var chicken = { //鸡
voice: function(){
console.log('嘎嘎嘎')
}
}

var Ducks = [] //鸭子类型
var joinDucks = function(animal){
if( animal && typeof animal.voice === 'function' ){
Ducks.push(animal)
console.log('恭喜加入鸭子类型')
}
}

joinDucks(duck) //恭喜加入鸭子类型
joinDucks(chicken) //恭喜加入鸭子类型

多态

对象继承类的属性/方法 但是可以对属性/方法进行针对性的改变
就是构造函数根据传入的变量不同,对同一方法,有不同的返回结果

1
2
3
4
5
6
7
8
9
var Animal = function( sound ){
this.sound = function(){
console.log(sound)
}
}
var duck = new Animal('嘎嘎嘎')
var chicken = new Animal('咯咯咯')
duck.sound() //嘎嘎嘎
chicken.sound() //咯咯咯

封装

封装的目的就是将信息隐藏 不在乎过程 只要能使用

异步请求-数据传递

解决了一个困扰了一天的bug

基本信息描述

1
2
3
4
5
就是一个创建主题的界面  
在没有进行操作的时候 第一次进入需要弹框进行填写 数据库用户名和密码等操作
填写完之后会返回一个数据库表名的数组
弹框是一个子组件 通过获取信息有没有存储在本地来判断是否是第一次操作


我的基本思路

  
1.当弹框信息填写完毕 提交之后 会请求接口获取 数据库表数组
2.请求成功之后 将数组信息存放到 sessionStorage 里面
3.通过 $emit在点击确定按钮的时候 把信息发送给父组件 关闭弹框
4.然后父组件就可以直接获取到信息并且渲染出来

bug描述

问题就在于 子组件是基于父组件而存在的  
父组件是渲染完成之后 才会显示的子组件
在点击确定按钮的时候 发送请求是异步的
所以会先执行$emit操作和弹框关闭操作
所以传递过去的值 是空的!!!

/**
在父组件 可以正常打印 但是就是不能渲染在页面上
一开始 我以为是因为传的是对象 不能渲染页面用 $set 但没有任何作用
然后我就觉得是父组件获取数据太快 用定时器的方法来异步获取数据 也不行

后面以为是传值的原因 用了vuex,refs啥啥啥的 都没有成功

后面在弹框组件内 保存按钮的函数下打印数据 发现打印的值为空!这个时候才定位到错误源
*/

解决

原因我猜想是请求接口是异步的,所以用$emit传递的时候 数据还没有被赋值
所以用setTimeout延迟操作了200ms
同样弹框状态也要和$emit一样延迟
然后父组件就可以正常获取到数据了

vue-element-admin中vuex多模块问题

store的配置文件

src/store/index.js

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
import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
Vue.use(Vuex)

// https://webpack.js.org/guides/dependency-management/#requirecontext
const modulesFiles = require.context('./modules', true, /\.js$/)

// you do not need `import app from './modules/app'`
// it will auto require all vuex module from modules file
// 大概理解下 不用去一个一个模块的导入 他已经写好了方法 来遍历modules文件下的各个子模块
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
// set './app.js' => 'app'
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
const value = modulesFiles(modulePath)
modules[moduleName] = value.default
return modules
}, {})

const store = new Vuex.Store({
modules, //引入全部子模块
getters
})

export default store

modules文件中存放的是各个小模块,分割store,每个模块内部都可以有自己的state、mutation、action、getter

src/store/modules/subject.js

1
2
3
4
5
6
7
8
9
10
11
12
13
const state = {
DBNames: []
}
const mutations = {
changedb(state, db) {
state.DBNames = db
}
}
export default {
namespaced: true, //独立的命名空间 模块内部的action、mutation、getter被注册在独立的命名空间内 false 则注册在全局
state,
mutations
}

使用方式:
①直接使用

在需要的 .vue 文件的<script>内的合适位置

1
2
3
4
5
6
//this.$store.state.模块名.变量名
this.$store.state.subject.DBNames

//this.$store.commit( '模块名/函数名' , 参数 )
this.$store.commit('subject/changedb','lll')


②配合辅助函数

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
import { mapState,mapMutations } from "vuex"

...

computed: {
...mapState({
name: state => state.subject.DBNames
})

...mapState('subject',{
name: state => state.DBNames
})
},

//使用 this.name

methods: {
...mapMutations({
dbChange: 'subject/changedb' // 改变的函数名:' 模块名/函数名 '
}),

...mapMutations('subject',{
dbChange: 'changedb' // 改变的函数名:' 模块名/函数名 '
}),
}
//使用 this.dbChange(参数)

③配合模块辅助函数使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { createNamespacedHelpers } from "vuex"
const { mapState,mapMutations } = createNamespacedHelpers('subject')

...
//调用方法和②一样
computed: {
...mapState({
name: state => state.DBNames
})
},
methods: {
...mapMutations({
dbChange: 'changedb'
}),
}
  • Copyrights © 2019-2026 John Doe
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信