js混淆基础,this,箭头函数,原型链深入理解
node js里面,window对象是global
js匿名函数:
1.(function(){})() //这样在浏览器控制台输出的是自己的返回值
2.!function(){}()
//下面这三种极其罕见
3.~function(){}()
4.-function(){}()
5.+function(){}()
// 第二种极其常见
// 公有属性和私有属性
function Person(name){
// 公有属性
this.name = name
this.age = 18
// 私有属性,除非直接调用构造函数,能调用私有属性,否则,访问不了
var evil = "evil"
var Nose = function(){
console.log("哈哈哈")
}
return name
}
Person("anlan") -->"evil"
m = new Person("anlan") -->Person {name: "anlan", age: 18}
function foo(){
console.log(this.a)
};
var obj = {a:1, foo};
var a = 2;
var foo2 = obj.foo;
var obj2 = {a:3, foo2:obj.foo}
// 打印的结果是1, this指向的是调用者obj
obj.foo()
// 打印的结果是2, 虽然foo2指向的是obj.foo,但是调用他的却是window
// foo2()发生了隐式丢失,调用者是window,使得foo()中的this指向window
foo2()
// 打印的结果是3,
// foo3()发生了隐式丢失, 使得foo()中的this指向obj2
obj2.foo2();
function foo(){
console.log(this.a)
}
function doFoo(fn){
console.log(this)
fn()
}
var obj = {a:1, foo}
var a = 2
var obj2 = {a:3, doFoo}
doFoo(obj.foo) // -> 2 this:window
obj2.doFoo(obj.foo) // -> 2
//所以说,如果你把一个函数当做参数传递到另一个函数的时候也会发生隐式丢失问题,且与包裹它的函数的this的指向无关,在非严格模式下,会把函数的this绑定到window上,在严格模式下会绑定到undefined
this永远指向最后调用他的对象,中间的不用去管
var obj1 = {
a:1
}
var obj2 = {
a:2,
foo1:function(){
console.log(this.a)
},
foo2:function(){
setTimeout(function(){
console.log(this)
console.log(this.a)
}.call(obj1), 0)
}
}
var a = 3
obj2.foo1() // ->2
obj2.foo2() // ->1 达到目的,指向的是obj1
obj2.foo2.call(obj1) // ->改变的是foo2函数内的this指向,foo2函数内的this指向和setTimeout里函数的this是没有任何关系的,因为调用定时器的始终是windows
var obj1 = {
a:1
}
var obj2 = {
a:2,
foo1:function(){
console.log(this.a)
},
foo2:function(){
function inner(){
console.log(this)
console.log(this.a)
}
inner()
}
}
var a = 3
obj2.foo1() // ->2
obj2.foo2() // ->3 this指向的是window
function foo(){
console.log(this.a)
}
var obj = {a:1}
var a = 2
foo() // 会正常打印window的2
foo.call(obj) // 由于foo.call(obj)显示绑定了this,所以会打印出obj下的a,也就是1
foo().call(obj) // 会报错,开始会执行foo()函数,会打印出2,但是会对foo()的函数的返回值进行.call()操作,可是foo()函数的返回值是undefined,因此就会报错了。
function foo(){
console.log(this.a)
return function(){
console.log(this.a)
}
}
var obj = {a:1}
var a = 2
foo() // ->2 2是foo函数输出的。虽然foo函数也返回了一个匿名函数,但是并没有调用他,只有写成foo()()才算是调用匿名函数
foo.call(obj) // ->1 1是foo.call(obj)输出的,由于.call是紧跟foo的所以改变的是foo()内this的指向,并且.call会使函数立即执行, 因此他也打印出1,同理,他也没有调用返回的函数
foo().call(obj)// ->2 1 ->2是foo().call(obj),先执行foo()打印出来的,此时foo()的this还是指向window. 再执行完foo()之后,会返回一个匿名函数,并且后面使用了.call(obj)来改变这个匿名函数的this指向并且调用了他。所以又输出了1
function foo(){
console.log(this.a)
return function(){
console.log(this.a)
}
}
var obj = {a:1}
var a = 2
foo() // 2
foo.bind(obj) // 2, 要有一个参数去接他,否则不会有什么用
foo().bind(obj) // f(){console.log(this.a)}
function foo(item){
console.log(item, this.a)
}
var a = 'window'
var arr = [1,2,3]
arr.filter(function(i){
console.log(i, this.a)
return i>2
}, obj)
// 如果我们没有传递第二个参数obj的话,this.a打印出来的肯定就是window下的a了,但是传入了之后将obj显示绑定到第一个参数函数上
//(关于arr.filter为什么会打印1,2,3虽然我们使用了return i>2 ,但是在执行过程中有console.log(),还是把每一项都打印出来了)
// filter函数用法
array.filter(function(currentValue,index,arr), thisValue)
var arr = ['A', 'B', 'C'];
var r = arr.filter(function (element, index, self) {
console.log(element); // 依次打印'A', 'B', 'C'
console.log(index); // 依次打印0, 1, 2
console.log(self); // self就是变量arr
return true;
});
this
永远指向的是最后调用他的那个对象- 匿名函数的
this
永远指向window
- 使用
.call()
或者.apply()
的函数是会直接执行的 bind()
是创建一个新的函数,需要手动调用才会执行- 如果
call,apply,bind
函数接受到的第一个参数为空或者null,undefined
的话,则会忽略这个参数 forEach,map,filter
函数的第二个参数也是能显示绑定this
的
// 浏览器特性问题:`window.name`不管页面如何刷新,都不会回收,直到页面关闭为止
function Person(name){
this.name = name
this.foo1 = function(){
console.log(this.name)
}
this.foo2 = function(){
return function(){
console.log(this.name)
}
}
}
var person1 = new Person('person1')
person1.foo1() // ->person1
person1.foo2()() //->
var name = 'window'
function Person(name){
this.name = name
this.foo = function(){
console.log(this.name)
return function(){
console.log(this.name)
}
}
}
var person2 = {
name: 'person2',
foo:function(){
console.log(this.name)
return function(){
console.log(this.name)
}
}
}
var person1 = new Person('person1')
person1.foo()() //person1 window
person2.foo()() //person2 window
//不懂
var name = 'window'
function Person(name){
this.name = name
this.foo = function(){
console.log(this.name)
return function(){
console.log(this.name)
}
}
}
var person1 = new Person('person1')
var person2 = new Person('person2')
person1.foo.call(person2)()
// 第一层person1.foo是普通函数,起初this指向的是person1,通过.call改变this的指向为person2,console打印输出person2,第二层return的function是匿名函数,this指向的是window,所以打印输出的是window
person1.foo().call(person2)
//第一层普通函数person1.foo()是普通函数,this指向的是person1,打印输出person1,执行之后,返回的function通过.call改变this的指向为person2,并立即执行,输出person2
// 没加()就不执行,加了()才执行
//person2 window
// person1 person2
// 提示:可以把new 来的person1看做下面的样子
var person1 = {
name:'person1'
foo:function(){
console.log(this.name)
return function(){
console.log(this.name)
}
}
}
箭头函数
箭头函数里面的this
是由外层作用域来决定的。且指向函数定义时的this
而非执行时,
箭头函数里面没有this
绑定,需要通过查找作用域链的方式,如果箭头函数被非箭头函数包含, 那么this
绑定的最近一层函数的this
,否则this
为未定义
var obj = {
name:'obj',
foo1:()=>{
console.log(this.name)
},
foo2:function(){
console.log(this.name)
return() =>{
console.log(this.name)
}
}
}
var name = 'window'
obj.foo1() //window 全局作用域,注意作用域只有全局作用域和函数作用域
obj.foo2()()//obj obj 这个返回值的匿名函数是箭头函数,`this`是由外层作用域决定的,而不是最后调用他的对象window
箭头函数的this
无法通过bind,call,apply
来直接修改,但是可以通过改变作用域中this
的指向来间接修改
var name = 'window'
var obj1 = {
name:"obj1",
foo1:function(){
console.log(this.name)
return() => {
console.log(this.name)
}
},
foo2:() =>{
console.log(this.name)
return function(){
console.log(this.name)
}
}
}
var obj2 = {
name:'obj2'
}
obj1.foo1.call(obj2)()
//第一层是一个普通函数,并且通过`.call改变this`的指向为`obj2`,第二层是箭头函数,他的`this`和外层作用域相同所以打印的也是`obj2`
obj1.foo1().call(obj2)
//`obj1.foo1()`打印出的是`obj1`,第二层是箭头函数想修改`this`的指向。但是是无效的,打印出的还是`obj1`。仔细看是箭头函数调用的`.call()`,所以无效
obj1.foo2.call(obj2)() // window window,第一层是箭头函数,想通过`.call`改变`this`的指向,改变不了,第二层是普通函数,`this`是最后调用他的`window`所以打印的也是window
obj1.foo2().call(obj2)//window obj2 第一层是箭头函数,外层作用域是`window`,所以打印的是`window`,第二层是普通函数,使用了`.call(obj2)`来改变了`this`的指向
-
字面量创建的对象作用域是
window
,如果里面有箭头函数属性的话,this
指向的是window
-
构造函数创建的对象,作用域可以理解为是这个构造函数,且这个构造函数的
this
,指向新建的对象的,因此this
指向这个对象 -
箭头函数的
this
无法通过bind,call,apply
来直接修改,但是可以通过改变作用域中this
的指向来间接修改