day39-ajax(下)

ajax同步异步

ajax是可同步可异步的一种技术。

在ajax对象调用open方法的时候,其实有第3个参数,值是布尔值,代表是否异步,默认为true。

当将第3个参数设置为false的时候,这次请求就变成了同步请求:

var xhr = new XMLHttpRequest;
// 省略了第三个参数:布尔值 - 代表当前请求是否异步 - 默认是true
// xhr.open('get','http://localhost:8888/test/first', true);
​
xhr.onreadystatechange = function(){
    if(xhr.readyState === 4){
        if(xhr.status>=200 && xhr.status<300){
            var res = xhr.responseText;
            console.log(res);
        }
    }
}
// 将第三个参数换成false - 当前请求也就从异步变成同步了
xhr.open('get', 'http://localhost:8888/test/first', false);
xhr.send()
console.log(111);

注意:要提前监听。建议使用异步,

ajax封装

// 封装
function ajax(obj){
    // 判断url
    // 判断是否传入
    if(obj.url === undefined){ // 没有传入
        // console.log('请求地址不能为空');
        // return
​
        // 抛出自定义错误
        throw new Error('请求地址不能为空') // 报错
    }
    // 传入了 - 判断类型
    if(Object.prototype.toString.call(obj.url) != '[object String]'){
        throw new Error('请求地址错误')
    }
​
    // 判断请求方式
    if(obj.method === undefined){
        obj.method = 'get'
    }
    //  限定请求方式必须是get或post
    if(obj.method.toLowerCase() != 'get' && obj.method.toLowerCase() != 'post'){
        throw new Error('请求方式只能是get或post')
    }
​
    // 将参数作为可选项 - 就在函数中判断,参数是否传入了
    // 不传入的时候,给obj设置默认的参数的值
    if(obj.isAsync === undefined){
        obj.isAsync = true
    }
    // 判断类型
    if(Object.prototype.toString.call(obj.isAsync) != '[object Boolean]'){
        throw new Error('isAsync必须是布尔值')
    }
​
    // obj.data作为可选项
    if(!obj.data){
        obj.data = null
    }
​
    // 判断success和error
    if(obj.success === undefined){
        obj.success = function(){}
    }
    if(Object.prototype.toString.call(obj.success) != '[object Function]'){
        throw new Error('success必须是函数')
    }
    if(obj.error === undefined){
        obj.error = function(){}
    }
    if(Object.prototype.toString.call(obj.error) != '[object Function]'){
        throw new Error('error必须是函数')
    }
​
    // 判断data - 如果参数较多,可以允许data以对象形式传入 - 转换成字符串放在send中
    var data = '' // 不管传入的是字符串还是对象,最终要在地址后面或send中使用的数据
    // 判断obj.data是否传入
    if(obj.data != undefined){ // 如果数据传入了
        // 判断类型
        if(Object.prototype.toString.call(obj.error) === '[object String]'){
            // 判断字符串数据中是否包含 =
            if(!obj.data.includes('=')){
                throw new Error('data数据是非法的!')
            }
            data = obj.data
        }else if(Object.prototype.toString.call(obj.error) === '[object Object]'){
            // 将对象转成字符串赋值给data变量
            var arr = []
            for(var key in obj.data){
                arr.push(key + '=' + obj.data[key])
            }
            data = arr.join('&')
        }else{
            // 既不是字符串也不是对象
            throw new Error('data必须是字符串或对象')
        }
        // 判断请求方式是否是get
        if(obj.method.toLowerCase() === 'get'){
            obj.url += '?' + data
        }
    }
​
    var xhr = new XMLHttpRequest;
    xhr.onreadystatechange = function(){
        if(xhr.readyState === 4){
            if(xhr.status>=200 && xhr.status<300){
                var res = xhr.responseText;
                obj.success(res)
            }else{
                obj.error()
            }
        }
    }
    xhr.open(obj.method, obj.url, obj.isAsync);
    if(obj.method.toLowerCase() === 'post' && data){
        xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
        xhr.send(data)
        return
    }
    xhr.send()
}

抛出自定义错误:

throw new Error(错误描述)

ajax请求文件

ajax除了可以请求服务器提供好的接口,还可以请求文件:任意文件 - 将文件中的内容读取出来 - 当前发送ajax的文件,必须是由http协议打开的。

ajax请求文件需要使用get请求,必须使用服务器打开当前页面。

ajax请求的文件类型中有两种比较特殊:

  • xml文件

    var xhr = new XMLHttpRequest;
    xhr.open('get','./test.xml');
    xhr.send()
    xhr.onreadystatechange = function(){
        if(xhr.readyState === 4){
            if(xhr.status>=200 && xhr.status<300){
                // 如果ajax请求的是xml文件 - 接收响应的数据 - xhr.responseXML
                // 获取到的是一个文档 - 类似于我们DOM中的document
                var res = xhr.responseXML
                // console.log(res);
                // 可以像操作dom一样去操作响应的xml文档
                var eles = res.getElementsByTagName('goods')
                // console.log(eles);
                // 遍历伪数组获取到每个数据
                for(var i=0;i<eles.length;i++){
                    // console.log(eles[i]);
                    var goodsnameTag = eles[i].getElementsByTagName('goodsname')[0]
                    // console.log(goodsnameTag);
                    console.log(goodsnameTag.innerHTML);
                }
            }
        }
    }

    xml文件的语法:

    一定要有一个文档声明:

    <?xml version="1.0" encoding="utf-8"?>

    xml文件中的标签都是自定义的 - 不能使用xml关键字作为标签名

    一定要有一个唯一的根标签

    标签可以有属性 - 属性的值一定是字符串

  • json文件

    var xhr = new XMLHttpRequest;
    xhr.open('get','test.json');
    xhr.send()
    xhr.onreadystatechange = function(){
        if(xhr.readyState === 4){
            if(xhr.status>=200 && xhr.status<300){
                var res = xhr.responseText;
                // res = JSON.parse(res)
                res = eval(res)
                console.log(res);
            }
        }
    }

    eval函数可以将一个字符串当做js代码执行。

    json文件的语法:

    json文件中必须是 一个 object/array

    json文件中的object的键,必须加双引号

    如果值是字符串,值也必须使用双引号

    数组或对象的最后一项后面,不允许加逗号

完善ajax封装:

function ajax(obj){
    if(obj.url === undefined) throw new Error('请求地址不能为空')
    if(Object.prototype.toString.call(obj.url) != '[object String]') throw new Error('请求地址错误')
    if(obj.method === undefined) obj.method = 'get'
    if(obj.method.toLowerCase() != 'get' && obj.method.toLowerCase() != 'post') throw new Error('请求方式只能是get或post')
    if(obj.isAsync === undefined) obj.isAsync = true
    if(Object.prototype.toString.call(obj.isAsync) != '[object Boolean]') throw new Error('isAsync必须是布尔值')
    if(!obj.data) obj.data = null
    if(obj.success === undefined) obj.success = function(){}
    if(Object.prototype.toString.call(obj.success) != '[object Function]') throw new Error('success必须是函数')
    if(obj.error === undefined) obj.error = function(){}
    if(Object.prototype.toString.call(obj.error) != '[object Function]') throw new Error('error必须是函数')
    var data = ''
    if(obj.data != undefined){
        if(Object.prototype.toString.call(obj.data) === '[object String]'){
            if(!obj.data.includes('=')) throw new Error('data数据是非法的!')
            data = obj.data
        }else if(Object.prototype.toString.call(obj.data) === '[object Object]'){
            var arr = []
            for(var key in obj.data){
                arr.push(key + '=' + obj.data[key])
            }
            data = arr.join('&')
        }else throw new Error('data必须是字符串或对象')
        if(obj.method.toLowerCase() === 'get') obj.url += '?' + data
    }
    if(obj.dataType === undefined) obj.dataType = 'json'
    if(obj.dataType.toLowerCase() != 'json' && obj.dataType.toLowerCase() != 'xml' && obj.dataType.toLowerCase() != 'text') throw new Error('dataType只接受json、text、xml')
    var xhr = new XMLHttpRequest;
    xhr.onreadystatechange = function(){
        if(xhr.readyState === 4){
            if(xhr.status>=200 && xhr.status<300){
                var res = '';
                switch(obj.dataType.toLowerCase()){
                    case 'json':
                        res = xhr.responseText
                        res = JSON.parse(res)
                    break;
                    case 'xml':
                        res = xhr.responseXML
                    break;
                    case 'text':
                        res = xhr.responseText
                    break;
                }
                obj.success(res)
            }else obj.error()
        }
    }
    xhr.open(obj.method, obj.url, obj.isAsync);
    if(obj.method.toLowerCase() === 'post' && data){
        xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
        xhr.send(data)
        return
    }
    xhr.send()
}
/// 调用模板 //
/*
ajax({
    url: '', // 请求地址 - 必填项
    method: '', // 请求方式 - 可选项,默认get
    data: '', // 请求主体 - 可选项 - 允许字符串/object
    isAsync: true, // 是否异步 - 可选项 - 默认true
    dataType: 'json', // 希望返回的数据格式 - 可选项 - 默认json - 允许json/xml/text
    success: res => {}, // 请求成功后要执行的函数 - 可选项 - 默认是一个匿名函数 - 会将请求回来的数据放在函数中作为参数
    error: function(){} // 请求失败会执行的函数 - 可选项 - 默认是一个匿名函数
})
*/

上下文调用模式

也叫作借用函数。

任何函数天生带有3个方法:

bind - 复制函数,返回一个新的函数,并将原函数中的this改变

bind语法:

函数.bind() // 复制函数 - 返回一个新的函数,新函数跟原函数一模一样

例:

function fn(){
    console.log(11);
}

var newFn = fn.bind() // 返回一个新函数
console.log(newFn); // 新函数跟原来的函数长的一模一样
console.log(fn === newFn); // 但是不是同一个

 

此时,这两个函数中的this是一样的

fn()
newFn()

 

改变this的语法:

函数.bind(this执行的对象) // 复制出来的新函数里面的this,就是参数 - 新的对象

例:

function fn(){
    console.log(11);
    console.log(this);
}
​
var obj = {
    name: '张三',
    age: 12
}
var newFn = fn.bind(obj) // 将fn复制一份成newfn,且newFn中的this代表obj
​
fn()
newFn()

 

使用场景:

// 使用场景
document.onclick = function(){
    // 原本的this代表window - 定时器中的this
    setTimeout(    function(){console.log(this);}  )
    // 定时器的函数是复制出来的新函数,且将其中的this改成了外面的this - document
    setTimeout(    (function(){console.log(this);}).bind(this)   )
}

call - 调用函数,调用过程中可以将原来函数中的this改成新的this

调用语法:

函数.call()

例:

function fn(){
    console.log(22);
    console.log(this);
}
​
fn()
console.log('------------------------------');
​
// 使用call来调用这个函数
fn.call()

 调用的时候顺便改变this的语法:

函数.call(对象) // 此时函数被调用了,但是其中的this被改成了对象

例:

function fn(){
    console.log(22);
    console.log(this);
}
​
fn()
console.log('------------------------------');
​
var obj = {
    name: '张三',
    age: 22
}
​
// 调用fn并将其中的this改成obj
fn.call(obj)

 

如果函数中有参数,那call方法在调用函数的时候,也应该传入参数:

函数.call(改变后的this指向, 函数的实参1, 函数的实参2, ...)

call在调用函数的时候,从call的第2个参数开始,跟fn的形参对应。

例:



function fn(a, b){
    console.log(a + b);
    console.log(this);
}
​
fn(1, 2)
console.log('-------------------------------------');
fn.call(null, 2, 3) // 没有改this但是给fn传参数了
// 改this变给fn传参
var obj = {
    name: '张三',
    age: 22
}
fn.call(obj, 3, 6)

 

使用场景:

精准的检测数据类型:Object.prototype.toString.call(新的数据)

将伪数组转成数组:Array.prototype.slice.call(伪数组)

可以简写为:

{}.toString.call(新的数据)
[].slice.call(伪数组)

apply - 调用函数,调用过程中可以将原来函数中的this改成新的this

调用函数语法:

函数.apply()

例:

function fn(){
    console.log(333);
    console.log(this);
}
​
fn()
console.log('---------------------------------------------');
// apply调用
fn.apply()

 调用并改this的语法:

函数.apply(对象) // 函数被调用了,且其中的this被改成了对象

例:

function fn(){
    console.log(333);
    console.log(this);
}
​
fn()
console.log('---------------------------------------------');
​
var obj = {
    name: '张三',
    age: 12
}
fn.apply(obj)

 

如果原函数中需要参数,apply在调用的时候也需要传入实参:

函数.apply(对象, [实参1, 实参2, ...]) // apply只有两个参数,第二个参数一定是一个数组,
                                    //数组中的每个元素跟原函数中的形参对应

例:

function add(a, b){
    console.log(a + b);
    console.log(this);
}
​
add(1, 2)
console.log('--------------------------------------------');
add.apply(null, [2, 3]) // 没有改this,并传入实参 - this还是window
var obj = {
    name: '张三',
    age: 12
}
// 调用并改this,并传入实参
add.apply(obj, [5, 3])

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值