重写与this有关的方法和关键字
call/apply/bind/typeof/new/instanceof 的实现
1.call/apply/bind
方法的调用:
1 2 3 4 5 6 7 8
| fn.call(obj, param1, param2, ...param3)
fn.apply(obj, [param1, param2, ...param3])
fn.bind(obj, param1, param2)(...param3);
|
call方法的实现
先看看call方法的具体调用案例
- 第一个参数为
null/undefined
时, this 指向 window
1 2 3 4
| function test() { console.log(this) } test.call();
|
- 第一个参数为
bool/number/string
类型时,this 指向 对应的包装类
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function test() { console.log(this) } test.call(1);
function test() { console.log(this) } test.call('1');
function test() { console.log(this) } test.call(false);
|
- 被调用的函数,会有一个返回值。并且内部的
this
指向第一个参数
1 2 3 4 5 6 7 8 9
| function test(a) { return a + this.name }
const obj = { name: 'JS' } const res = test.call(obj, 'Hello '); console.log(res)
|
实现思路:
- 判断是否有第一个参数,有则使用对象进行包装,否则为
window
。
- 原方法内部的
this
要指向 当前传入的 第一个参数。
- 特性:谁调用方法,方法内部的
this
就指向谁;this => 原方法
- 使用一个变量,保存this,以便后面调用它
- 将重写
call
方法的 arguments
参数 传入到 原方法中,并执行该方法。
- 利用evel方法执行;
- 最后将执行结果返回。
最终代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Function.prototype.myCall = function(ctx) { ctx = ctx ? Object(ctx) : window; ctx['originFn'] = this; const args = [] for (let i = 1; i < arguments.length; i++) { args.push('arguments[' + i + ']') } let res = eval("ctx.originFn(" + args + ")") delete ctx.originFn; return res; }
|
apply 方法的实现
与call方法高度相似,但有一下几点区别:
- 如果第二个参数为
object/function/null/undefined
,则arguments.length = 0
- 第二个参数为
string/number/boolean
类型,会报错: Uncaught TypeError: CreateListFromArrayLike called on non-object
- 只取第二个参数,且必须为数组
实现思路:
判断是否有第一个参数,有则使用对象进行包装,否则为window
。
原方法内部的 this
要指向 当前传入的 第一个参数。
- 特性:谁调用方法,方法内部的
this
就指向谁;this => 原方法
- 使用一个变量,保存this,以便后面调用它
判断第二个参数的类型,如果不是数组则做一下处理:
string/number/boolean
,则抛出异常。
object/function/null/undefined
, 执行原始方法并返回结果,结束执行。
将重写 apply
方法的 arguments
参数 传入到 原方法中,并执行该方法。
- 利用evel方法执行;
利用evel方法执行;
最终代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| function typeOf (value) { if(value) { const key = ({}).toString.call(value); const res = { '[object String]': 'String', '[object Number]': 'Number', '[object Boolean]': 'Boolean', '[object Object]': 'Object', '[object Array]': 'Array', '[object Function]': 'Function', } return res[key]; } else { return typeof value; } }
Function.prototype.myApply = function (ctx, args) { ctx = ctx ? Object(ctx) : window; ctx.originFn = this; if ( (typeof args === 'string') || (typeof args === 'number') || (typeof args === 'boolean') ) { throw TypeError('CreateListFromArrayLike called on non-object') } if (!args || typeOf(args) !== 'Array') { const res = ctx.originFn(); delete ctx.originFn return res; }
const tempArgs = []; for (let i = 0; i < args.length; i++) { tempArgs.push('args[' + i + ']'); } const res = eval('ctx.originFn(' + tempArgs + ')'); delete ctx.originFn; return res; }
|
bind 方法的实现
特点:
- fn.bind()方法返回一个新函数,且fn方法不执行;
- bind方法接收一部分参数,返回的新函数接收一部分参数;
- 返回的新函数
newFn
的 this
指向 bind(obj)
传递的对象
- 实例化返回的新函数,this 指向原函数fn构造出来的新实例
- 实例应该继承fn构造函数函数上的原型
最终代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| Function.prototype.myBind = function(thisObj) { let firstFn = this; let args = [].slice.call(arguments, 1);
let newFn = function () { let newArgs = [].slice.call(arguments);
return firstFn.apply(this instanceof newFn ? this : thisObj, args.concat(newArgs)); }
let _tmpFn = function () {} _tmpFn.prototype = this.prototype; newFn.prototype = new _tmpFn();
return newFn; }
function test(user, car) { console.log(user + '买了一辆' + car); console.log(this, arguments); }
test.prototype.color = '红色'
console.log('=== myBind ==='); let T = test.myBind(obj2, '张三'); let newTest = new T('玛莎拉蒂'); console.log(newTest.color);
|
2.typeof 关键字
typeof的限制
总共只有6中,ES6之后有symbol
1 2 3 4 5 6 7 8 9
| typeof 'a' typeof 1 typeof true typeof undefined typeof {} typeof null typeof new RegExp() typeof [] typeof function(){}
|
最终代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| function typeOf (value) { if(value) { const key = ({}).toString.call(value); const res = { '[object String]': 'String', '[object Number]': 'Number', '[object Boolean]': 'Boolean', '[object Object]': 'Object', '[object Array]': 'Array', '[object Function]': 'Function', '[object Null]': 'Null', '[object Undefined]': 'Undefined', '[object RegExp]': 'RegExp', '[object Date]': 'Date', } return res[key]; } else { return typeof value; } }
|
3.new 关键字
构造函数的特点:
- 未实例化时,构造方法内部的this为 undefined(非严格模式为window);
- 实例化之后,构造方法内部的this 是一个空对象;
- 实例化之后,构造方法内部的this的prototy 指向 构造函数的 prototype;
- 实例化之后,会接收构造方法的返回值。
1 2 3 4 5 6 7 8 9
| function Test() { console.log(this) } Test()
new Test()
|
1 2 3 4 5 6 7 8
| Test.prototype.add = function () { return this.a + this.b; } function Test() { console.log(this) } new Test()
|
下图为上述的输出结果:
最终代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function myNew () { const constructor = [].shift.call(arguments); const _this = {};
Object.setPrototypeOf(_this, constructor.prototype); const res = constructor.apply(_this, arguments);
return typeOf(res) === 'Object' ? res : _this; }
const test = myNew(Test, 1, 2); test.add();
|
4.instanceof
特点:
- 判定 target 是否是 Type的实例
- target 的原型 是否继承自 Type
最终代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function instanceOf(target, type) { type = type.prototype; target = Object.getPrototypeOf(target)
while (true) { if (target === null) return false; if (target === type) return true; target = Object.getPrototypeOf(target) } }
[] instanceof Array instanceOf([], Array)
[] instanceof Object instanceOf([], Object)
|
-
Post title: 重写与this有关的方法和关键字
-
Post author: Chao
-
Create time: 2021-10-08 13:15:00
-
Post link: 2021/10/08/与this有关的方法和关键字/
-
Copyright notice: All articles in this blog are licensed under BY-NC-SA unless stating additionally.
$tools-item-width = 2.2rem
$tools-item-font-size = 1.1rem
$tools-item-border-radius = 0.1rem
.side-tools-container {
position relative
.tools-item {
width $tools-item-width
height $tools-item-width
margin-bottom 0.2rem
color var(--text-color-3)
font-size $tools-item-font-size
background var(--background-color-1)
border-right none
border-radius $tools-item-border-radius
box-shadow 0.1rem 0.1rem 0.2rem var(--shadow-color)
cursor pointer
i {
color var(--text-color-3)
}
&:hover {
color var(--background-color-1)
background var(--primary-color)
box-shadow 0.2rem 0.2rem 0.4rem var(--shadow-color)
i {
color var(--background-color-1)
}
}
+keep-tablet() {
width $tools-item-width * 0.9
height $tools-item-width * 0.9
margin-bottom 0.2rem
font-size $tools-item-font-size * 0.9
}
&.rss {
a {
width 100%
height 100%
border-radius $tools-item-border-radius
&:hover {
color var(--background-color-1)
background var(--primary-color)
box-shadow 0.2rem 0.2rem 0.4rem var(--shadow-color)
}
}
}
}
.side-tools-list {
transform translateX(100%)
opacity 0
transition-t("transform, opacity", "0, 0", "0.2, 0.2", "linear, linear")
&.show {
transform translateX(0)
opacity 1
}
}
.exposed-tools-list {
if (hexo-config('style.scroll.percent') == true) {
.tool-scroll-to-top {
display none
&.show {
display flex
}
&:hover {
.percent {
display none
}
.arrow-up {
display flex
}
}
.arrow-up {
display none
}
.percent {
display flex
font-size 1rem
}
}
}
}
}
$icon-size = 1.2rem
$search-header-height = 3rem
.search-pop-overlay {
position fixed
top 0
left 0
z-index $z-index-8
display flex
width 100%
height 100%
background rgba(0, 0, 0, 0)
visibility hidden
transition-t("visibility, background", "0, 0", "0.3, 0.3", "ease, ease")
&.active {
background rgba(0, 0, 0, 0.35)
visibility visible
.search-popup {
transform scale(1)
}
}
.search-popup {
z-index $z-index-6
width 70%
height 80%
margin auto
background var(--background-color-1)
border-radius 0.4rem
transform scale(0)
transition-t("transform", "0", "0.3", "ease")
+keep-tablet() {
width 80%
}
+keep-mobile() {
width 90%
}
.search-header {
display flex
align-items center
height $search-header-height
padding 0 1rem
background var(--text-color-6)
border-top-left-radius 0.2rem
border-top-right-radius 0.2rem
.search-input-field-pre {
margin-right 0.2rem
color var(--text-color-3)
font-size 1.3rem
cursor pointer
}
.search-input-container {
flex-grow 1
padding 0.2rem
.search-input {
width 100%
color var(--text-color-3)
font-size 1.2rem
background transparent
border 0
outline 0
&::-webkit-search-cancel-button {
display none
}
&::-webkit-input-placeholder {
color var(--text-color-4)
font-size 1rem
}
}
}
.close-popup-btn {
color var(--text-color-3)
font-size $icon-size
cursor pointer
&:hover {
color var(--text-color-1)
}
}
}
#search-result {
position relative
display flex
box-sizing border-box
height 'calc(100% - %s)' % $search-header-height
padding 0.3rem 1.5rem
overflow auto
.search-result-list {
width 100%
height 100%
font-size 1rem
li {
box-sizing border-box
margin 0.8rem 0
padding 0.8rem 0
border-bottom 0.1rem dashed var(--border-color)
&:last-child {
border-bottom none
}
.search-result-title {
position relative
display flex
align-items center
margin-bottom 0.8rem
padding-left 1rem
font-weight bold
&::after {
position absolute
top 50%
left 0
width 0.4rem
height 0.4rem
background var(--text-color-3)
border-radius 50%
transform translateY(-50%)
content ''
}
}
.search-result {
margin 0
padding-left 1rem
line-height 2rem
word-wrap break-word
}
a {
&:hover {
color var(--text-color-3)
}
}
.search-keyword {
color var(--primary-color)
font-weight bold
border-bottom 0.1rem dashed var(--primary-color)
}
}
}
#no-result {
margin auto
color var(--text-color-4)
}
}
}
}