澳门游戏平台大全个人属性
函数的作用域与this指向是js中很重要的一部分,理清这点东西需要个逻辑,看看我的逻辑怎么样…
相关链接:
下面是个提纲,可以直接挑你感兴趣的条目阅读。
JS面向对象(1) —
简介,入门,系统常用类,自定义类,constructor,typeof,instanceof,对象在内存中的表现形式
• 函数的定义方式:直接定义,对象的方法,对象原型的方法;
JS面向对象(2) —
this的使用,对象之间的赋值,for…in语句,delete使用,成员方法,json对象的使用,prototype的使用,原型继承与原型链
• 函数的调用方式:直接调用,call/apply,with
JS面向对象(3) —
Object类,静态属性,闭包,私有属性,
call和apply的使用,继承的三种实现方法
•
对于直接定义的函数和对象的方法,作用域默认状态下是它的定义处的作用域链。
1.Object类
• 对于直接定义的函数,this指向window。
在JS中,Object是所有类的基类,使用Object类来创建自定义对象时,可以无需定义构造函数(constructor,prototype,hasOwnProperty(property))
• 对于对象的方法,this指向实例化对象。
1 var per = new Object();
2 per.name = 'zhangsan';
3 per.age = 30;
4 alert(per.name + per.age);
• 用call/apply改变方法的this指向
我们想在程序中得到一个对象变量,只要能存储大量数据即可,这个时候,我们可以考虑使用Object类。Object类避免了对构造器的定义。
Object类下另一个常用的属性:hasOwnProperty
• 在函数或方法的定义时可以通过with改变其作用域链。
1 var per = new Object();
2 per.name = 'zhangsan';
3 per.age = 30;
4 if per.hasOwnProperty('email'){
5 alert('具有email');
6 }else{
7 alert('无email');
8 }
下面分开来具体说说:
函数的定义,如提纲中提到的可以分为两种:直接定义,对象的方法。从下面的示例代码中可以看到函数fn1与fn2以及对象的方法doFunction在函数使用name时name的值来自相应的域。
2.静态属性
var name = 'window下的name
';var resultCon;function fn1() { resultCon.innerHTML += name;}function MyObj() { var name = 'MyObj下的name
'; this.doFunction = function() { resultCon.innerHTML += name;
在有些面向对象的语言当中,可以使用static关键字定义类的静态属性或者静态方法,在JS中,可以进行模拟。
在使用name的值时将“name”用“this.name”来代替会出现什么情况呢,看下例:
语法:
var name = 'window下的name
';var resultCon;function fn1() { resultCon.innerHTML += this.name;}function MyObj() { var name = 'MyObj下的name
'; this.doFunction = function() { resultCon.innerHTML += this.name;
类名.属性名
从结果来看可以验证提纲中的第4和5条,也可以看到this和作用域是两套分离的链,遵循个自的变量查询逻辑,具体的查询逻辑在下面的性能分析中会提到,如果是新手建议先看一下“js的作用域链”方面的基础知识。
类名.属性=function(){}
关于函数的调用方法,我用下面的方示例说明提纲中的第2、6条:
1 function Person(){
2 }
3 Person.count = 0;
4 var p1 = new Person();
5 Person.count++;
6 var p2 = new Person();
7 Person.count++;
8 var p3 = new Person();
9 Person.count++;
10 alert(Person.count);
var name = 'window下的name
';var resultCon;function fn1() { resultCon.innerHTML += this.name;}function MyObj() { var name = 'MyObj下的name
'; this.doFunction = function() { resultCon.innerHTML += this.name;
添加静态属性和静态方法:
调用时call和apply的使用是为了改变被调用函数的this指向。with的使用是为了改变被调用函数中变量的查询域。我们把上例中的call和name前的this去掉再加上with来演示with的作用。
1 function Person(){
2 Person.count++; //静态属性
3 Person.getCount=function(){ //静态方法
4 alert('当前共有' + Person.count + '个人');
5 }
6 }
7 Person.count = 0;
8 var p1 = new Person();
9 var p2 = new Person();
10 var p3 = new Person();
11 Person.getCount();
var name = 'window下的name
';var resultCon;function fn1 { with { resultCon.innerHTML += name; }}function MyObj { var name = 'MyObj下的name
';
看到with的使用并不方便,需要在被调用函数中添加with,有人可能想能不能向下面那样调用来整体改变变量作用域而不去改变被调用函数呢?
3.闭包
with ; fn2(); var obj = new MyObj;}
概念:所谓闭包,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因此这些变量也是该表达式的一部分。
很遗憾,不可以!所以在一些成熟的框架中随处可见call和apply的使用,却很少用到with,在用JSHint检测js语法的时候with处都标了小红点,在一些js编码指导中也建议尽量少用with,因为with改变了变量的默认查询链,所以会给后期的维护人员一些困惑,还有性能方面的一些考虑,请慎用with。
提出一个问题:
以上这篇深入理解js函数的作用域与this指向就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。
1 function display(){
2 var i=10;
3 }
4 display();
5 //在这里,想访问局部变量i
在全局中,不能访问局部变量i,因为作用域不同,而且,在display函数执行完毕后,局部变量i会被回收。
闭包的功能:“访问局部变量”和“使变量所占的内存不被释放”
1 //例1
2 function fn1(){
3 function fn2(){
4 alert('hello');
5 }
6 return fn2; //返回fn2函数首地址
7 }
8 var test=fn1(); //test也指向了fn2函数的首地址
9 test();
通过例1我们知道:变量是可以指向函数的首地址的,函数也可以返回另一个函数的首地址。
1 //例2
2 function fn1(){
3 var i = 10;
4 function fn2(){
5 alert(i);
6 }
7 return fn2; //返回fn2函数首地址
8 }
9 var test=fn1(); //test也指向了fn2函数的首地址
10 test();
通过例2我们知道:使用一个拒不函数包含变量i,这样局部变量i的内存不会被回收。
1 //例3
2 function fn1(){
3 var i = 10;
4 function fn2(){
5 alert(i++);
6 }
7 return fn2; //返回fn2函数首地址
8 }
9 var test=fn1(); //test也指向了fn2函数的首地址
10 test();
11 test();
12 test();
在例3中,因为i的内存永远不会被回收,所以每次调用fn2,i的值会+1。运行的结果是弹出10,弹出11,弹出12。
闭包的原理:在例3中,共有三个作用域:全局作用域,fn1的作用域,fn2的作用域。在全局作用域里有test=fn1(),其实这句话就相当于test=fn2。在fn1作用域里有
var
i=10和return fn2,在fn2作用域例有alert(i++)。当全局作用域下的test=fn1()执行时,test指向了fn2的作用域,这个时候fn2作用域下的i被全局作用域钩住,根据作用域链的法则,fn2下并没有定义i,所以在fn2下的i往上一层作用域上找,找到了fn1作用域下的var i=10。所以全局的test钩住了fn2的i,fn2的i钩住了fn1的i,所以fn1运行完毕后,不会被回收。
4.私有属性
在面向对象思想中,对于有些敏感的,不想公开的成员可以定义为私有的,在JavaScript中可以模拟这个功能。
语法:
function Person(p_name){
var name = p_name;
this.age
}
var :私有
this :公有
1 function Person(p_name,p_age){
2 this.name = p_name;
3 var age = p_age;
4 }
5 var p1 = new Person('zhangsan',30);
6 alert(p1.name);
7 alert(p1.age);
在上面这个例子中,我们想用var来表示私有成员属性,但Person构造函数执行完毕后,age会被回收,不能当做成员属性来使用。
1 function Person(p_name,p_age){
2 this.name = p_name;
3 var age = p_age;
4 this.setAge=function(a){
5 age = a;
6 }
7 this.getAge=function(){
8 return(age);
9 }
10 }
11 var p1 = new Person('zhangsan',30);
12 p1.setAge(20);
13 alert(p1.getAge());
this.setAge和this.getAge两个方法使用到了局部变量age,所以age不会被回收。
如果只有set方法,说明该属性是只写属性。
如果只有get方法,说明该属性是只读属性。
5.call和apply的使用
call和apply的功能:使用指定的对象调用当前函数。call和apply的功能完全相同,只是在语法上略有不同。
语法:
call([thisObj[,arg1[,arg2[,argN]]]])
第一个参数:函数执行时,this指向谁
后面的参数:根据需要顺序指定
apply([thisObj[,argArray]])
第一个参数:函数执行时,this指向谁
第二个参数:数组,表示参数集合
在js中,函数有几种调用形式:
Person(); //Person内的this指向window
var p1=new Person(); //Person内的this指向p1
per.Person(); //Person内的this指向per
1 function Person(p_name,p_age){
2 this.name = p_name;
3 this.age = p_age;
4 }
5 function speak(){
6 alert(this.name + this.age);
7 }
8 var p1 = new Person('zhangsan',30);
9 //speak(); 这样调用this指向window
10 //p1.speak(); p1对象没有speak属性
使用call和apply来调用
1 function Person(p_name,p_age){
2 this.name = p_name;
3 this.age = p_age;
4 }
5 function speak(){
6 alert(this.name + this.age);
7 }
8 var p1 = new Person('zhangsan',30);
9 speak.call(p1);
10 speak.apply(p1);
call和apply在执行时做了两件事:1)将函数内部this指向了第一个参数
2)调用函数
另外:还可以这样解决问题:
P1.say=speak;
P1.say();
这样解决和上面解决方法有本质上的区别:
上面的解决办法是直接调用speak函数,只不过函数内部this的指向发生改变。
下面的解决办法会为p1对象增加属性,p1对象的“体积”会变大。
举例说明:
1 <script>
2 function fn1(){
3 this.style.color='red';
4 }
5 function fn2(){
6 this.style.fontSize='50px';
7 }
8 window.onload=function(){
9 document.getElementById('btn').onclick=function(){
10 var div1 = document.getElementById('div1');
11 fn1.call(div1);
12 fn2.apply(div1);
13 };
14 };
15 </script>
16 <div id='div1'>hello javascript</div>
17 <input type='button' id='btn' value='确定'>
6.继承的三种实现方法
概念:在有些面向对象语言中,可以使用一个类(子类)继承另一个类(父类),子类可以拥有父类的属性和方法,这个功能可以在js中进行模拟。
三种方法:
第一种:扩展Object方法
1 Object.prototype.方法=function(父类对象){
2 for(var i in 父类对象){
3 this[i] = 父类对象[i];
4 }
5 };
举例说明:
1 Object.prototype.ext=function(parObject){
2 //循环遍历父类对象所有属性
3 for(var i in parObject){
4 //为子类对象添加这个遍历到的属性
5 //它的值是父类对象这个属性的属性值
6 this[i] = parObject[i];
7 }
8 }
9 function Person(p_name,p_age){
10 this.name=p_name;
11 this.age=p_age;
12 this.speak=function(){
13 alert(this.name+this.age);
14 }
15 }
16 function Student(p_no){
17 this.no=p_no;
18 this.say=function(){
19 alert(this.no+this.name_this.age);
20 }
21 }
22 var stu = new Student(101);
23 stu.ext(new Person('xiaoqiang',20));
24 stu.speak();
25 stu.say();
第二种:使用call和apply方法
语法:
父类构造器.call(this,…….);
1 function Person(p_name,p_age){
2 this.name=p_name;
3 this.age=p_age;
4 this.speak=function(){
5 alert(this.name+this.age);
6 }
7 }
8 function Student(p_no,p_name,p_age){
9 this.no=p_no;
10 this.say=function(){
11 alert(this.name+this.age+this.no);
12 }
13 Person.call(this,p_name,p_age);
14 }
15 var stu = new Student(8,'zhagsan',18);
16 stu.speak();
17 stu.say();
第三种:原型继承
语法:
子类.prototype = new 父类();
1 function Person(p_name,p_age){
2 this.name=p_name;
3 this.age=p_age;
4 this.speak=function(){
5 alert(this.name+this.age);
6 }
7 }
8 function Student(p_no){
9 this.no=p_no;
10 this.say=function(){
11 alert(this.name+this.age+this.no);
12 }
13 }
14 Student.prototype = new Person('wangwu',21);
15 var stu = new Student(10);
16 stu.speak();
17 stu.say();
标签:之家, 作用, 函数, 澳门游戏平台大全, 脚本