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 一样。