call的实现
先看下call的实例
let person = {
name: 'SpwaN'
}
function getName() {
console.log(this.name)
}
getName.call(person) // SpwaN
call的特点:
1. 改变this指向
2. 直接调用函数
3. 传null的时候,指向window
4. 可以传参,参数分开传
ES6 写法
Function.prototype.call1 = function (context, ...args) {
var context = context || window // 解决第三步
context.fn = this // 解决第一步
context.fn(...args) // 解决第二步,第四步
delete context.fn // 处理小尾巴
}
ES5 写法
Function.prototype.call2 = function (context) {
var context = context || window
context.fn = this
let args = []
for (let i = 0; i < arguments.length; i++) {
args.push('arguments[' + i + ']')
}
eval('context.fn(' + args + ')')
delete context.fn
}
apply 的实现
apply 的特点:
1. 改变this指向
2. 直接调用函数
3. 传null的时候,指向window
4. 可以传参,参数是个数组
ES6 写法
Function.prototype.apply1 = function (context, args) {
var context = context || window
context.fn = this
args ? context.fn(...args) : context.fn()
delete context.fn
}
ES5 写法
Function.prototype.apply1 = function (context, arr) {
var context = context || window
context.fn = this
if (!arr) {
context.fn()
} else {
let args = []
for (let i = 1; i < arr.length; i++) {
args.push('arguments['+ i +']')
}
eval('context.fn('+ args +')')
}
delete context.fn
}
bind的实现
bind的特点
1. 改变this指向
2. 返回一个函数
3. 传null的时候,指向window
4. 可以传参,参数分开传,也可以先传一部分,再调用函数的时候再传一部分
5. 返回的函数如果当做构造函数的话,那bind中的this将无效,但参数依然生效
// 第一版 实现this绑定和传参 (未实现当把return的函数当做构造函数时候的this绑定失效)
Function.prototype.bind1 = function (context) {
var _this = this
// 拿到除绑定对象之后的参数
var args = Array.prototype.slice.call(arguments, 1)
return function () {
// 拿到返回函数的所有参数
var newArgs = Array.prototype.slice.call(argument)
return _this.apply(this, args.concat(newArgs))
}
}
// 第二版
Function.prototype.bind2 = function (context) {
var _this = this
var args = Array.prototype.slice.call(arguments, 1)
var middlewares = {}
var f = function () {
var newArgs = Array.prototype.slice.call(arguments)
// 如果this等于实例,那么就把this绑定到实例上,如果不是就把bind上绑定的this添加过来
_this.apply(this instanceof f ? this : context, args.concat(newArgs))
}
middlewares .prototype = this.prototype
// new一个中间函数 防止修改f的原型而改变了this的原型
f.prototype = new middlewares()
return f
}
// 第三版 ES6写法
Function.prototype = function (context, ...args) {
let _this = this
let obj = {}
let f = function (...newArgs) {
return _this.apply(this instanceof f ? this : context, [...args, ...newArgs])
}
obj.prototype = this.prototype
f.prototype = new obj()
return f
}
jsonp的实现
jsonp的特点:
创建script 通过src来获取数据 后台用FunctionName包裹数据 调用前台设置的FunctionName方法
/**
* opts:
* - param { String } (callback)
* - prefix { String } (__jp)
* - name { String } (prefix + incr)
* - timeout { Number } (60000)
* @param {String} url fetch url
* @param {Object} opts 配置项
* @param {Function} fn callback
*/
function originJsonp (url, opts, fn) {
let prefix = opts.prefix || '__jp'
let id = opts.name || (prefix + '0')
let param = opts.param || 'callback'
let target = document.getElementsByTagName('script')[0] || document.head
let script
// 清除script 及 定时器
function clear () {
if (script.parentNode) {
script.parentNode.removeChild(script)
}
window[id] = {}
}
window[id] = function (data) {
clear()
fn && fn(data)
}
// 不用判断 > -1 因为~-1 === 0
url += (~url.indexOf('?') ? '&' : '?') + param + '=' + encodeURIComponent(id);
url = url.replace('?&', '?');
script = document.createElement('script')
script.src = url
target.parentNode.insertBefore(script, target)
}
function jsonp (url, opts) {
return new Promise((resolve, reject) => {
originJsonp(url, opts, (data) => {
resolve(data)
})
})
}
jsonp('https://sug.so.360.cn/suggest?encodein=utf-8&encodeout=utf-8&format=json&fields=word&word=%E6%88%91', {
'prefix': 'suggest_so'
}).then(res => {
console.log(res)
})