1、ES5面向对象编程:
request.js: 一个文件一个模块
function Service() {
this.request = function(opts) {
this.opts = {...Service.DEFAULTS, ...opts}
if (window.aladdin && window.aladdin.http && window.aladdin.http.request) {
return this._aladdinRequest()
} else if (window.$ && window.$.ajax) {
return this._ajaxRequest()
} else { // 原生请求
return this._xhrRequest()
}
}
this.get = function (opts) {
return this.request((opts.method = "GET") && opts)
}
this.post = function (opts) {
return this.request((opts.method = "POST") && opts)
}
}
Service.DEFAULTS = {
url: '',
method: 'GET',
headers: { // HTTP信息头对象
contentType: 'application/x-www-form-urlencoded;charset=utf-8'
},
timeout: 15000, // 请求超时设置,毫秒
data: {}
}
/**
* aladdin发送请求
*/
Service.prototype._aladdinRequest = function () {
const config = {
url: this.opts.url,
method: this.opts.method,
headers: this.opts.headers,
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) {
window.aladdin.http.request(config, function (error, res) {
// 请求失败
if (error) {
reject({
status: error.code, //错误代码
statusText: error.message //具体错误信息
})
return
}
// 请求成功
if (res.status === 200) {
isJsonStr(res.body) ? resolve(JSON.parse(res.body)) : resolve(res.body) //res.body 响应原始内容
} else {
reject({
status: res.status, //响应状态码,如 401
statusText: res.statusText //响应状态码对应的文本,如 'Unauthorized'
})
}
})
})
}
/**
* ajax发送请求
*/
Service.prototype._ajaxRequest = function () {
let config = {
url: this.opts.url,
type: this.opts.method,
data: this.opts.data, // 如果是GET请求,它会自动被作为参数拼接到url上
headers: this.opts.headers,
// dataType: 'json', // 默认none。假若设置json,请求成功200但返回类型为text,会造成解析错误走error回调
timeout: this.opts.timeout,
xhrFields: {
withCredentials: true
}
}
return new Promise(function(resolve, reject) {
// 请求成功
config.success = function(data, status, xhr) {
if (xhr.status === 200) {
isJsonStr(data) ? resolve(JSON.parse(data)) : resolve(data) //data 响应原始内容
} else {
reject({
status: xhr.status, // 响应状态码
statusText: xhr.statusText // 状态码对应的文本
})
}
}
// 请求失败 (超时,解析错误,或者状态码不在HTTP 2xx)
config.error = function(xhr) {
reject({
status: xhr.status, // 响应状态码
statusText: xhr.statusText // 状态码对应的文本
})
}
window.$.ajax(config)
})
}
/**
* XMLHttpRequest发送请求
*/
Service.prototype._xhrRequest = function () {
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(let 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 window.ActiveXObject("Microsoft.XMLHTTP")
}
xhr.timeout = this.opts.timeout
xhr.withCredentials = true
xhr.open(method, url, true)
for(let key in this.opts.headers) {
xhr.setRequestHeader(key, this.opts.headers[key])
}
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({
status: xhr.status, // 响应状态码
statusText: xhr.statusText // 状态码对应的文本
})
}
}
}
xhr.onerror = xhr.ontimeout = function() {
reject({
status: xhr.status, // 响应状态码
statusText: xhr.statusText // 状态码对应的文本
})
}
xhr.send(method === 'POST' ? data : null)
})
}
/**
* 检查字符串是否JSON文本
* @param {String} value 字符串
* @return {Boolean} 是否JSON文本
*/
var isJsonStr = function isJsonStr(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
}
例外:
事件加在标签上,<div onclick="fn"/>,则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 一样。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。