JavaScript语言精粹2

1、ES5面向对象编程:

request.js: 一个文件一个模块

function Service() {
  this.request = function(opts) {
    this.opts = Object.assign({}, Service.DEFAULTS, opts)

    if (window.aladdin && aladdin.http && aladdin.http.request) {
      return this._aladdinRequest()
    } else if (window.$ && $.ajax) {
      return this._ajaxRequest()
    } else { // 原生请求
      return this._xhrRequest()
    }
  }
}

Service.DEFAULTS = {
  url: '',
  method: 'GET',
  contentType: 'application/x-www-form-urlencoded;charset=utf-8',
  dataType: 'json', // 期待返回数据类型
  timeout: 15, // 请求超时设置
  data: {},
  errorCallback: null, // 统一失败回调
  successCallback: null // 统一成功回调
}

// aladdin request
Service.prototype._aladdinRequest = function () {
  console.log('请求工具-->', 'aladdin');
  const config = {
    url: this.opts.url,
    method: this.opts.method,
    headers: {
      'Content-Type': this.opts.contentType
    },
    timeout: this.opts.timeout,
    xhrFields: {
      withCredentials: true
    }
  }
  if (config.method.toUpperCase() === 'POST') {
    config.body = this.opts.data
  } else {
    config.qs = this.opts.data
  }

  return new Promise(function(resolve, reject) {
    aladdin.http.request(config, function (error, res) {
      if (error) { // 调用失败
        // aladdin.toast.show({
        //   message: '系统异常,请稍后再试!'
        // })
        reject('系统异常,请稍后再试!')
        return
      }
      if (res.status === 200) {
        isJsonStr(res.body) ? resolve(JSON.parse(res.body)) : resolve(res.body) // res.body 响应原始内容
      } else {
        reject(res.status + res.statusText) // 响应状态码 + 状态码对应的文本(如 200 -- OK)
      }
    })
  })
}

// ajax request
Service.prototype._ajaxRequest = function () {
  console.log('请求工具-->', '$.ajax');
  let config = {
    url: this.opts.url,
    type: this.opts.method,
    data: this.opts.data, // get请求时,会自动序列化参数后添加到url末尾
    headers: {
      'Content-Type': this.opts.contentType
    },
    dataType: this.opts.dataType,
    timeout: this.opts.timeout,
    xhrFields: {
      withCredentials: true
    }
  }

  return new Promise(function(resolve, reject) {
    // when request succeeds
    config.success = function(data, status, xhr) {
      if (xhr.status === 200) {
        resolve(data) // data: 响应原始内容
      } else {
        reject(xhr.status + xhr.statusText) // 响应状态码 + 状态码对应的文本(如 200 -- OK)
      }
    }
    //  if there is an error (timeout, parse error, or status code not in HTTP 2xx)
    config.error = function(xhr, textStatus) {
      reject('系统异常,请稍后再试!')
    }
    $.ajax(config)
  })
}

// XMLHttpRequest request
Service.prototype._xhrRequest = function () {
  console.log('请求工具-->', 'XMLHttpRequest');
  let url = this.opts.url
  const method = this.opts.method.toUpperCase()
  const data = this.opts.data
  // 如果是 GET 请求,需要处理 data 里的数据并且以一定的规则拼接到 url 后
  if (method === 'GET') {
    var formData = []
    for(key in this.opts.data) {
      formData.push(''.concat(key, '=', this.opts.data[key]))
    }
    let params = formData.join('&')
    if (params.length > 0) {
      url += url.indexOf('?') >= 0 ? ''.concat('&', params) : ''.concat('?', params)
    }
  }

  let xhr = null
  if (window.XMLHttpRequest) {
    xhr = new XMLHttpRequest()
  } else if (window.ActiveXObject) {
    xhr=new ActiveXObject("Microsoft.XMLHTTP")
  }
  xhr.timeout = this.opts.timeout
  // xhr.responseType =  'json' // 指定类型与服务器返回值类型若是兼容的,则按指定类型返回,若不兼容则返回null
  xhr.withCredentials = true
  xhr.open(method, url, true)
  xhr.setRequestHeader('Content-Type', this.opts.contentType)

  return new Promise(function(resolve, reject) {
    xhr.onreadystatechange = function() {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
          isJsonStr(xhr.response) ? resolve(JSON.parse(xhr.response)) : resolve(xhr.response) // xhr.response,整个响应体
        } else {
          reject(xhr.status + xhr.statusTxt) // 响应状态码 + 状态码对应的文本(如 200 -- OK)
        }
      }
    }
    xhr.onerror = function(e) {
      reject('系统异常,请稍后再试!')
    }
    xhr.ontimeout = function(e) { }
    xhr.send(method === 'POST' ? data : null)
  })
}

// 检查是否JSON文本
const isJsonStr = function(value){
  try {
    eval('('+value+')')
    return true
  } catch(er){
    return false
  }
}


var req = new Service()

// var p = req.request({
//   url: '/hget',
//   method: 'GET',
//   data: { name: 'zhangsan' }
// })

var p = req.request({
  url: '/hput',
  method: 'PUT',
  data: { name: 'zhangsan' }
})

// var p = req.request({
//   url: '/hpost',
//   method: 'POST',
//   data: { name: 'zhangsan' }
// })
p.then(res => console.log('resolved', res)).catch(err => console.log('rejected', err))
// module.exports = {
//  service: new Service()
// }

2、JavaScript的this指向
透彻认识function的this在不同调用环境下的指向

  • 事件调用环境: 谁触发事件,函数里面的this指向的就是谁。
var element = document.querySelector('.app')
element.onclick = function() { 
  this.style.left = '100px' // this指向element
}
例外:
事件加在标签上,
,则this指向全局对象。
  • 全局环境: window(浏览器环境) module.exports(node环境)
console.log(this === module.exports) // true
module.exports  = {
    num: 10,
    fn: function() {
        console.log(this.num) // 10
        console.log(this === module.exports) // true
    }
}
module.exports.fn()

在node中执行输出:
true
10
true
  • 函数内部环境: this最终指向的是调用它的对象。当函数被多层对象包含,如果函数被最外层对象调用,this指向的也只是它上一层的对象。
var obj = {
    'sub': {
         fn: function() {
            console.log(this)
         }
    }
}
obj.sub.fn() // this指向sub对象
var test = obj.sub.fn
test() // this.指向window对象
  • 构造函数: 如果构造函数中有return,如果return的值是对象,this指向返回的对象,如果不是对象,则this指向保持原来的规则。在这里,null比较特殊。
function fn() { 
  this.num = 10; 
  return { num: 20 }  // { num: 20 } 
  return [1,2,3]      // [1,2,3]
  return 0      // {num: 10}
  return 'abc'  // {num: 10}
  return true   // {num: 10}
  return null   // {num: 10}
}
console.log(new fn()) 
  • 箭头函数: 箭头函数本身是没有this和arguments的,在箭头函数中引用this实际上调用的是上一层作用于的this,且箭头函数的this指向在声明时就已经决定了。这里强调一下上一层作用域,因为对象是不能形成对立的作用域的。
var obj = {
  fn: () => {
    console.log(this)
  },
  fn1: function() {
    console.log(this)
  }
}
obj.fn() // this指向Window,因为对象不形成作用域
obj.fn1() // this指向obj对象

解决事件函数里面内层函数取不到触发事件对象办法:
function move() {
    setTimeout(function() { 
      console.log(this) // this指向Window,setTimeout方法省略了Window.setTimeout  
    }, 1000)
    setTimeout(() => { 
      console.log(this) // this指向触发事件对象element
    }, 1000)
}
element.onclick = move 
  • 修改this指向

* 无法修改箭头函数this指向

var box = document.querySelector('.box')
var obj = {
    fn: () => {
      console.log(this)
    }
}
obj.fn.call(box) // Window,证明箭头函数的this指向不可修改,在定义时已经确定。

* 修改普通函数this指向:call apply bind 用法大同小异
var obj = {
  name: '小王',
  age: 20,
  fn: function(fm, t) {
    console.log(this.name + '年龄 ' + this.age, '来自' + fm + '去往' + t)
  }
}
var db = {
  name: '德玛',
  age: 99
}

obj.myFun.call(db,'成都','上海');     // 德玛 年龄 99  来自 成都去往上海
obj.myFun.apply(db,['成都','上海']);      // 德玛 年龄 99  来自 成都去往上海  
obj.myFun.bind(db,'成都','上海')();       // 德玛 年龄 99  来自 成都去往上海
obj.myFun.bind(db,['成都','上海'])();   // 德玛 年龄 99  来自 成都, 上海去往 undefined

从上面四个结果不难看出:
call 、bind 、 apply 这三个函数的第一个参数都是 this 的指向对象,第二个参数差别就来了:
call 的参数是直接放进去的,第二第三第 n 个参数全都用逗号分隔。
apply 的所有参数都必须放在一个数组里面传进去。
bind 除了返回是函数以外,它 的参数和 call 一样。

你可能感兴趣的:(javascript)