一、手写Bind
函数
bind函数:bind() 方法创建一个新的函数, 当这个新函数被调用时其this置为提供的值,其参数列表前几项置为创建时指定的参数序列。它的一个用处就是用来改变函数this指向。
总的来说bind有如下三个功能点:
1.改变原函数的 this 指向,即绑定上下文,返回原函数的拷贝
2.当 绑定函数 被调用时,bind的额外参数将置于实参之前传递给被绑定的方法。
3.注意,一个 绑定函数 也能使用 new 操作符创建对象,这种行为就像把原函数当成构造器,thisArg 参数无效。也就是 new 操作符修改 this 指向的优先级更高。
TIS:关于this
This的四种绑定规则
1.默认绑定
指向全局对象
2.隐式绑定
绑定到这个上下文对象
3.显示绑定
运用apply call 方法,在调用函数时候绑定this,也就是指定调用的函数的this值
4.new绑定
就是使用new操作符的时候的this绑定
上述四条规则优先级由上到下依次递增。
BIND函数:
输入:接受一个或者多个参数,第一个是要绑定的上下文,额外参数当作绑定函数的前置参数。
输出:返回原函数的拷贝,即返回一个函数,这个函数呢具备原函数的功能
// 定义这个方法为myBind
Function.prototype.myBind = function(thisArg) {
if (typeof this !== 'function') {
return;
}
var _self = this;
var args = Array.prototype.slice.call(arguments, 1) //从第二个参数截取
return function() {
return _self.apply(thisArg, args.concat(Array.prototype.slice.call(arguments))); // 注意参数的处理
}
}
到此实现上下文功能以及参数功能还不能实现NEW关键词功能
我们先考虑一下new操作符在调用构造函数时做了哪些操作?
比如说 var a = new b()
{} .__proto__ = b.prototype
所以我们做了如下修改:
Function.prototype.myBind = function(thisArg) {
if (typeof this !== 'function') {
return
}
var _self = this
var args = Array.prototype.slice.call(arguments, 1)
var fnNop = function () {} // 定义一个空函数
var fnBound = function () {
var _this = this instanceof _self ? this : thisArg
return _self.apply(_this, args.concat(Array.prototype.slice.call(arguments)))
}
// 维护原型关系
if (this.prototype) {
fnNop.prototype = this.prototype;
}
fnBound.prototype = new fnNop();
return fnBound;
}
二、new操作符具体干了什么
var
obj = {};
第一行,我们创建了一个空对象obj
obj.__proto__ = Base.prototype;
第二行,我们将这个空对象的__proto__成员指向了Base函数对象prototype成员对象
Base.call(obj);
将构造函数的this通过apply、call指向中间对象。返回该中间对象,也就是返回了实例对象
三、前端路由
(1). 分类
浏览器支持的两种前端路由方式分别是hash、history。通俗的讲带#的就是hash路由。
优缺点对比:hash
优点:兼容性好,不许要服务端支持
缺点:带#,不符合人类审美,会被一些有洁癖的甲方爸爸diss
history
优点:符合人类审美
缺点:开发成本,需要服务端支持。有些前端不太了解nginx配置或者SpringMVC路由的写法,后端不支持的话只能自己搞,不过这是个一劳永逸的工作。
(2)原理
hash路由
-
当url的hash改变时,即浏览器的地址栏#后面的内容改变时,页面不会刷新,浏览器不会像后端发送页面请求。前端要做的就是改变hash和监听hashchange事件。
-
更改hash的代码:
window.location.hash = '#a'
window.location.replace('#b')
上面两行代码可以随便在哪个网站打开控制台试一下。
-
监听hashchange代码:
window.addEventListener('hashchange', () => document.getElementById('root').innerHTML = 'hash has change')
history路由
-
HTML5 规范中提供了 history.pushState 和 history.replaceState 来进行路由控制。 通过这两个方法,可以实现改变 url 且不向服务器发送请求。
-
更改history的代码:
history.pushState({}, '', '/a')
history.replaceState({}, '', '/b')
上面两行代码可以随便在哪个网站打开控制台试一下。
-
监听history change:
这个有点麻烦,不像hash一样有一个现成的hashchange方法。考虑到更改history只有两种方式:浏览器的前进后退、通过上面两行代码触发,所以可以对这两种方式进行回调处理:
-
浏览器的后退/前进:触发popstate事件
-
window.addEventListener('popstate', handleHref);
-
history.pushState/history.replaceState:同步事件,直接把popstate的回调函数写在后面就可以。
history.pushState({}, '', '/a');
handleHref();
-
回调函数:
function handleHref () {document.getElementById('app').innerHTML = 'history has change' }