引入
首先运行如下js代码
var obj={
color:"red", //这个color是对象的属性,不是函数形参
changecolor:function(color){
obj.color=color //给对象属性更新值
}
}
obj.changecolor("blue")
console.log(obj.color)
//结果为blue
可以看出当给一个对象设置成员值时:如果已经有这个成员了就会覆盖之前值
var obj={
createcolor:function(color){
obj.color=color //给对象创建新属性
}
}
obj.createcolor("blue")
console.log(obj.color)
//结果为blue
我们又可以看出给一个对象设置属性值时:如果没有这个属性就会创建一个新的属性并赋值
而且对象花括号里冒号后面保存的不是函数就是基本属性,保存的函数就称之为方法
var obj={
width:"200px",
height:"200px",
color:"red",
onclick:function(color){
var obj={name:"jack",age:20}
obj.color=color //blue就近,给上面的obj创建color属性并赋值blue
}
}
obj.onclick("blue")
console.log(obj.color) //这里的obj是外面的obj
//运行结果为red
从上面的代码我们可以看到obj.color=color是对onclick函数里面的obj运行的,所以创建了一个color属性并赋值blue,而console.log(obj.color)则是对onclick函数外面的obj来说的,所以打印结果是red
this的定义与作用
this代表的是执行这个this代码的环境对象,this在脚本中代表的是window全局对象
这并不便于我们理解,我们简单的看如下代码
console.log(this)
//运行结果为window
function fn() {
console.log(this)
}
fn()
//运行结果为window
var obj = {
name: "Karen",
sayname: function () {
console.log(this)
}
}
obj.sayname()
//运行结果为{name:“Karen”,...}
可以很清楚的看到上面打印的是window,下面打印的是obj这个对象
我们可以用一个规律,两个步骤解释一下这句话
1.找到距离this最近的一个function(嵌套级)
2.看是谁调用这个function函数,this就是这个函数的调用者
在js程序中无论多复杂的程序this只需要看离最近(嵌套而不是平级关系)的function的这个单词的调用者
如上面的fn()==>window.fn() this最近的function是fn,fn的调用者是window,所以this就是window
同理,函数sayname的调用者是obj,所以this是obj
举例
再举几个例子
var obj={
name:"芭比",
son:{
name:"儿子",
say:function(){
console.log(this)
}
}
}
obj.son.say()
打印的是son这个对象{name: '儿子', say: ƒ},调用者是obj.son
var obj={
name:"芭比",
son:{
name:"儿子",
say:function(){
console.log(this)
}
}
}
var a=obj.son.say
a()
obj.son.say赋给a,a()调用者为window,this是window
function fn(){
return function fm(){
console.log(this)
}
}
var obj={}
obj.fm=fn()
console.log(obj)
obj.fm()
fn()() //window
定义对象obj
为obj增加成员属性fm,并将fn运行结果fm函数赋给它
打印obj {fm:f}
运行obj的fm方法,this的调用者为obj,打印obj {fm:f}
fn()() this的调用者为window,运行结果为window
可以总结出以下常见函数调用形式的规律
运行函数 this调用者
fn() // window
obj.fn() // obj
obj.xx.xx2() // obj.xx
(function(){})() // window
fn()() // window
fn()[1]() // fn()返回的数组
例题
下面我们来看几道题
1.
var name = "ilil"; //window本来就有name属性,值为空白
var obj = {
name: "huahua",
prop: {
name: "xixi",
getname: function () {
return this.name
}
}
};
console.log(obj.prop.getname()); //调用者为obj.prop
var test = obj.prop.getname; //将函数赋给test
console.log(test()); //test()调用者为window
首先打印的是obj.prop.getname(),调用者为obj.prop,this.name就是obj.prop.name,就是xixi
接着将obj.prop.getname函数赋值给test,test是全局对象,所以test()的调用者是window,this.name打印的是window的name属性,就是lili
2.
var object={
who:"world",
greet(){
return this.who;
}
};
console.log(object.greet())
object.greet() 得到返回值 this.who,this指向的是object,object.who的值为world
3.
function Foo() {
getName = function () { //运行到这没有getname变量,就去全局作用域里找
console.log(1);
};
return this;
}
var getName = function () {
console.log(4);
};
Foo().getName(); //foo结果是window,window的属性getname
1.Foo()调用时,需要使用getName变量,但是Foo内部没有这个变量,就去外面的全局作用域找
2.找到了之后就继续运行,将function(){console.log(1)},这个匿名函数赋值给getName,函数Foo的返回值是this,这里的this是window
3.Foo().getName() ==》window.getName() 上一步骤,getName的值已经改变了,所以console.log打印的是1,而不是4
4.
加深一下对 对象所指向的内存空间的理解
var a={n:1};
var b=a;
a.x=a={n:2};
console.log(a.x); //undefined
console.log(b.x); //{x:2}
var a=b=c;
a是最后赋值(引用)的,但是a的引用声明是一开始就有了
我们知道上述规律就可以分析
1. a.x先取空间,但是不赋值,这里取的空间是{n:1,x:}里的a.x
2. a={n:2} 由{n:1,x:}指向另一个内存空间{n:2}
3. 此时a.x={n:2}这个赋值赋给原来的内存空间{n:1,x:}中,即为{n:1,x:{x:2}}
4. 此时a不指向{n:1,x:{n:2}}这个内存空间指向{n:2},由b=a的b指向{n:1,x:{x:2}}
补充
关于全局对象window
function fn(){}
var a=20
console.log(fn) //console.log(window.fn)
console.log(a) //console.log(window.a)
var a=20 全局变量会在脚本执行时,把变量名设置为全局对象window的属性
function fn(){console.log(this)}全局函数会在脚本执行时,把函数设置为全局对象window的方法
所以在调用时a,其实是window.a,函数fn同理
var a=this;
console.log(a);
function fn(){
console.log(this)
}
fn() //window.fn() 对象调用方法
/*运行结果
window
window
*/
关于函数的两种写法
// 函数的写法其实有两种
// 1.声明函数:直接在作用域写一个函数
function fn(){
}
// 2.定义一个函数:直接创建一个函数,把它当作数据一样
var a=function(){}
var obj={
say:function(){}
}
var arr=[20,function(){},300]