javascript面向对象编程(OOP)——汇总
目录
-
一、JS的解析与执行过程
- 预处理阶段
- 执行阶段
-
二、作用域
- 块作用域
- 函数作用域
- 动态作用域
- 词法作用域
-
三、闭包
- 什么是闭包
- 闭包的好处
-
四、函数与对象
- 对象
-
函数
-
原型(prototype)
-
this
-
new的理解
-
五、封装
-
六、继承
-
七、多态
-
八、项目实战minijQuery
一、JS的解析与执行过程
1.1、预处理阶段
注意:js预处理阶段会扫描所有var声明的变量,把var声明的变量或函数存放到词法作用域里,如果是变量初始值为“undefined”,如果是函数则指向函数;
全局(window)
词法作用域(Lexical Environment):顶级的Lexical Environment是window;
1、先扫描函数声明后扫描变量(var声明);
2、处理函数声明有冲突,会覆盖;处理变量声明时有冲突,会忽略。
函数
词法作用域(Lexical Environment):每调用一次,产生一个Lexical Environment;
1、先函数的参数:比如arguments(函数内部对象,代表函数实参,可通过下标获取调用函数时传的实参);
2、先扫描函数声明后扫描变量(var声明);
3、处理函数声明有冲突,会覆盖;处理变量声明时有冲突,会忽略。
1.2、执行阶段
1、给预处理阶段的成员赋值
2、如果没有用var声明的变量,会成为最外部LexicalEnvironment的成员(即window对象的变量)
函数内部对象:arguments
<script> /*提示:*/ // arguments是每一个函数内部的一个对象 // 可以访问实际传递给函数的参数的信息。 // 声明的时候参数的个数与实际调用时无关 function add(a,b){ console.log(add.length);// 形参的个数 console.log(arguments.length);// 实际传过来的参数 var total = 0; for(var i = 0;i< arguments.length;i++){ total += arguments[i]; } return total;// 返回实参的总和 } // 调用时传的实参 var result = add(1,2,3,4,5); var result2 = add(1,2); console.log(result);// 15 console.log(result2);// 3 </script>
二、作用域
提示:js的作用域不是块级别的;js的作用域是函数级别的。
2.1、块作用域
2.2、函数作用域
2.3、动态作用域
2.4、词法作用域
代码示例:
<script> //js作用域 // 定义:用来查找变量的值的规则集;决定一个变量的范围 // 提示:js的作用域不是块级别的;js的作用域是函数级别的。 // javascript使用的是词法作用域,它的最重要的特征是它的定义过程发生在代码的书写阶段 /*以下js代码用立即调用写法(私有化),避免变量冲突*/ //1、块作用域:代码在花括号里面有效(js没有块作用域) (function(){ for(var i=0;i<5;i++){ var a = i ; } // 在花括号外面可以访问到i,a console.log(i);// 5 console.log(a);// 4 })(); //2、函数作用域:代码在function()函数的花括号里面有效 (function(){ var message = "函数外部的"; function fn(){ var message = "函数内部的"; console.log(message);// 函数内部的 } console.log(message);// 函数外部的 })(); //3、动态作用域:在运行时决定(是this指向的表现;谁调用,this指向谁);动态作用域其实是指this的词法作用域 // 动态作用域并不关心函数和作用域是如何声明以及在任何处声明的,只关心它们从何处调用。 // 换句话说,作用域链是基于调用栈的,而不是代码中的作用域嵌套 (function(){ var a = 2; function foo() { console.log( a ); } function bar() { var a = 3; foo();// 此时this===window } bar();// 2 })(); /* var a = 2; bar = { a:3, foo:function(){ console.log(this.a); } } bar.foo();//3 */ //4、词法作用域:词法作用域(也称为静态作用域或闭包) // js的作用域解析,用new Function创建函数 (function(){ // 闭包 var a = 2; function bar() { var a = 3; return function(){ console.log(a);// 此时捕获a=3 }; } var foo = bar(); foo();// 3 })(); // 如果处于词法作用域,也就是现在的javascript环境。变量a首先在foo()函数中查找,没有找到。于是顺着作用域链到全局作用域中查找,找到并赋值为2。所以控制台输出2 // 如果处于动态作用域,同样地,变量a首先在foo()中查找,没有找到。这里会顺着调用栈在调用foo()函数的地方,也就是bar()函数中查找,找到并赋值为3。所以控制台输出3 //小结:两种作用域的区别,简而言之,词法作用域是在定义时确定的,而动态作用域是在运行时确定的 </script>
三、闭包(Closure)
3.1、什么是闭包
由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数,内部函数并访问父函数的局部变量”。
理解闭包:
1、闭包可以理解为一个对象,里面包含函数以及被函数捕获的变量 , 一个圈里包含函数与捕获的变量
2、也可以只把函数捕获的变量称之为闭包。
<script> //如何写会产生闭包 function P(){ var a = 5; var b = 6; return function C(){ console.log(b);//此时捕获变量b,值为6 } } var result = P(); result();// 6 </script>
产生闭包的条件:
1、函数内部包含子函数;
2、子函数访问父函数的变量;
3.2、闭包的好处
用途:一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
<script> // 闭包实例 function Person(){ var age = 1; this.getAge = function(){ return age ; } this.setAge = function(val){ age = val; } } var p = new Person(); p.setAge(20); console.log(p.getAge()); </script>
代码示例:
<script type="text/javascript"> /*闭包--理解*/ // 提示:this由运行时决定! // 题目一:理解r1与r2的输出 function addFactory(){ var adder = 5; return function(data){ adder += data;// 此时adder变量是闭包捕获到的值 return adder; } } var adder1 = addFactory(); var r1 = adder1(1);//6 r1 = adder1(1);//7 var adder2 = addFactory(); var r2 = adder2(2);//7 r2 = adder2(2);//9 // 题目二:下面的代码输出什么 var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ return function(){ return this.name;// 输出"The Window";this = window; //return object.name;// 输出"My object" }; } }; //alert(object.getNameFunc()());// The Window // 理解二: var fun = object.getNameFunc();// 返回一个函数,此时函数this指向window;window.fun() alert(fun());// 所以,输出是:"The Window" // 题目三: var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ var that = this;//this = object; return function(){ return that.name;//闭包捕获父函数的that,that = object; }; } }; alert(object.getNameFunc()());// My Object // 理解三: // var obj = object.getNameFunc(); // alert(obj());// 此时函数由于内部的name是object调用 </script>
四、函数与对象
4.1、对象
4.2、函数
4.3、原型(prototype)
javascript对象部署:
JavaScript是一种通过原型实现继承的语言;在JavaScript中所有都是对象,原型(prototype)也是一个对象,通过原型可以实现对象的属性继承;
prototype:在js中是函数特有的属性;指向该函数的原型(this.prototype)
__proto__:在js中是所有对象都有的属性;指向此对象的构造器(函数)的原型对象(prototype)
<!-- 对象都有这属性(找对象的父类对象):__proto__; 只有函数有的属性(找函数的原型,是个对象):prototype; 对象(或函数)的构造器(顶级构造器时Function()):constructor; --> <script> function Aaa(){} //undefined var aaa = new Aaa(); //undefined aaa.__proto__; //{constructor: ƒ} Aaa.prototype; //{constructor: ƒ} aaa.__proto__ === Aaa.prototype; //true aaa.constructor; //ƒ Aaa(){} Aaa.constructor; //ƒ Function() { [native code] } aaa.constructor.constructor //ƒ Function() { [native code] } </script>
原型理解:
1. 函数Foo的__proto的值等于Foo.prototype,对吗? 错,函数Foo.__proto__===Function.prototype,函数Foo的实例的__proto__属性的值等于函数Foo.prototype 2.Object的prototype可以修改吗?能与不能原因是什么? //可以,函数(对象)的原型可以任意修改或继承 不可以,因为Object.prototype是只读,所以不能赋值;但可以修改或添加 3. 顶级constructor是谁? Function() 4.顶级原型对象是谁? Object 5.对象的construtor成员是个属性还是个方法? 可以是属性,也可以是方法(一般不建议这么写,耗资源,在每次new是都要执行很多代码) 6.Function有没有__proto__,为什么?值等于Object.prototype吗? 1.有(是对象都有),与prototype相等,因为Function是顶级构造器,所以,函数的__proto__属性指向的构造器原型是与Function.prototype相等; 2.不等于,Function.prototype与Function.__proto__指向function.prototype 7.所有的构造器的__proto__都等于其对应的prototype 错,等于Function.prototype;因为对象的__proto__属性指向的是对象的构造器的prototype(函数的构造器是Function()) 8.创建类形式的继承的四部曲是什么? 1.创建父类 2.创建子类 3.确定继承关系:A.prototype = Object.create(B.prototype); 4.修改构造器(因为继承后的构造器指向是父类的原型指向的构造器,也就是说,子类的原型指向的构造器===父类的原型指向的构造器) 9.Function的constructor于prototype值可以修改吗? 不可以,Function是顶级构造器,Function.__proto__指向Function.prototype 10.Object.prototype === Object.__proto__吗? 不相等,Object.prototype是object.prototype;Object.__proto__是function.prototype 11. Function.prototype === Function.__proto__吗? 相等,(因为Function是顶级构造器,__proto__指向Function.prototype)Function.prototype===Function.__proto__ 12. function F(){}; var f1 = new F(); f1.__proto__ === Object.prototype吗? 不对,f1.__proto__ === F.prototype;f1.__proto__.__proto__ === Object.prototype;
4.5、this
原则:
1、this由运行时决定!
2、函数中 this 到底指向谁 , 由调用此函数时的对象决定 , 而不是由定义函数所在的对象决定。
在JavaScript中this表示:谁调用它,this就是谁。
如何改变this指向:
call:
apply:
<script> /* var data = {}; Array.prototype.push.call(data,100,200); Array.prototype.push.apply(data,[1,2,3,8,10]); console.log(data); */ </script>
4.6、new的理解
简单的可以理解为:new改变了this指向的对象;
五、封装
六、继承
七、多态
八、项目实战minijQuery
<script type="text/javascript"> /* // 提示: // 暴露外部使用的一个接口 var jQuery = window.jQuery = window.$ = function(selector){ return new jQuery.fn.init(selector); } // 处理原型对象 jQuery.fn = jQuery.prototype = {} jQuery.fn.init.prototype = jQuery.fn; // 实现继承,并且只处理只有一个参数,也就是插件的扩展 jQuery.extend = jQuery.fn.extend = function(){} // 添加静态方法 jQuery.extend({}); // 添加实例方法 jQuery.fn.extend({}); // 1.获取节点对象 var jq1 = jQuery(".pp"); 或 var jq1 = jQuery.fn.init(".pp"); */ // 提供全局访问接口($()、jQuery()) (function () { /// 暂时把window的全局变量存起来,用做处理变量冲突 var _$ = window.$; var _jQuery = window.jQuery; //暴露外部使用的一个接口(获取节点对象) var jQuery = window.jQuery = window.$ = function(selector){ return new jQuery.fn.init(selector);// init.prototype; }; //处理原型对象 jQuery.fn = jQuery.prototype = { init:function(selector){ var elements = document.querySelectorAll(selector); Array.prototype.push.apply(this,elements); return this; }, version:"1.0.0", length:0, size:function(){ return this.length; } }; // jQuery.fn.init.prototype === init.prototype; // jQuery.prototype; jQuery.fn.init.prototype = jQuery.fn; //实现继承,并且只处理只有一个参数,也就是插件的扩展 jQuery.extend = jQuery.fn.extend = function(){ var o = arguments[0]; for(var p in o){ this[p] = o[p]; } }; /// 测试:(继承方法) // var obj = {name:"张三三"} // var jq = $(".pp"); // jq.extend(obj); //添加静态方法 jQuery.extend({ trim:function(text){ return (text||"").replace(/^\s+|\s+$/g,"");// 替换text字符串的开头和结尾匹配任何空白字符为空(即,替换开头和结尾的空格字符为空) }, noConflict:function(){ window.$ = _$; window.jQuery = _jQuery; return jQuery; } }); /// 测试:(命名冲突) // var jq = jQuery.noConflict();//返回一个jQuery函数,解决与全局的jQuery属性冲突 // var obj = jq(".pp"); //添加实例方法 jQuery.fn.extend({ get:function(num){ return this[num]; }, each:function(fn){ for(var i = 0 ;i< this.length; i++){ fn(i,this[i]); } return this; }, css:function(){ var l = arguments.length; if(l == 1){ return this[0].style[arguments[0]]; } else { var name = arguments[0]; var value = arguments[1]; this.each(function(index,ele) { ele.style[name] = value; }); } return this; } }); })(); </script>