5-14、面向对象

tech2022-08-19  79

面向对象

对代码的一种抽象,对外统一提供调用接口的编程思想

怎么来的:基于原型的面向对象中,对象则是依靠构造器利用原型构造出来的。

属性:事物的特性 方法:事物的功能 对象:事物的一个实例 原型:js函数中有prototype属性引入的一个对下个,即原型对象

闭包

闭包是指有权访问另一个函数作用域中的变量的函数

闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数)

c指向的是函数b,那么c();,就访问到了i。

特点:函数b 是在 a内嵌套的,函数a需要返回函数b 用途:1、读取函数内部的变量 2、让 i 变量的值 保留在内存中。

//也可以这样 function a(){ i = 999; function b(){ return i; } return b; } c = a()(); console.log(c); //还可以这样 function a(){ i = 999; function b(){ return i; } b(); return b; } console.log(a());

案例2:

//已知存在内存中的证明案例 function a(){ i = 999; nadd = function(){ i+=1; } function b(){ console.log(i); } return b; } c = a(); c(); // 999 nadd(); // 这里执行了i+1 c(); // 1000

优点:有利于封装,可以访问局部变量 缺点:内存占用浪费严重,内存泄漏,容易被黑客攻击

js字面式对象声明

var xxx = { 属性名 : 属性值, 属性名 : 属性值, 属性名 : 属性值, ... 方法名称 : function(){}, 方法名称 : function(){}, ... }

实际案例:

var xy = { name : "xy", age : 90, eat : function(fds){ alert("我在吃" + fds); } } console.log(xy.name); xy.eat("汉堡");

第二种方法: 案例:

var xy = new Object; xy.name = "xxx"; xy.age = 99; xy.eat = function(fds){ alert("我在喝" + fds); }; console.log(xy.name); var a = xy.eat("可乐"); //这里不能直接调用,需要赋值 console.log(a);

第三种方法: js中构造方法声明对象: 案例:

function xy (name,age,eat){ this.name = name, this.age = age, this.eat = eat, this.show = function add(){ console.log(this.name+ "--" +this.age + this.eat); } } var obj1 = new xy("xy",99,"吃"); console.log(obj1.name); //上面参数传进去了,这里可以用xy. obj1.show(); //show本身没有参数,那就要obj带着参数进去了 var obj2 = new xy("xx",00,"喝"); //能够多次调用且不冲突 obj2.show();

工厂模式

按照某种模式,可以不断地创造对象。

function stus(name,age){ var xy = new Object(); xy.name = name; //xy.name属性 name参数 xy.age = age; xy.run = function(){ return this.name + this.age; } xy.say = function(){ console.log("第二句。"); } return xy; //最后要返回一下对象 } var stu1 = stus("张三",14); var stu2 = stus("李四",16); console.log(stu1.name); //张三 stu1.say(); //第二句。 alert(stu2.run()); //记得带括号 //stu1 和 stu2 没有任何关联

执行的时候从上往下,先stus载入内存,然后执行stu1,然后找到stus,执行里边的语句,最后返回xy。

构造和工厂模式不同: 1、构造方法不会显示创建对象,将属性赋值给this,不需要return对象 2、工厂模式在方法内部创建Object对象,返回Object对象,属性和方法都是赋给Object对象。

js原型模式声明对象

prototype 可以利用它,在对象外部添加属性。 instanceof检查是否包含

function xy (){} xy.prototype.name = "xx"; xy.prototype.age = 16; xy.prototype.add = function(){ console.log(this.name + this.age); } xy.prototype.qqq = function(){ console.log("第二句话。"); } var stu1 = new xy(); stu1.qqq();

第二种方法:

//json形式 function xy (){} xy.prototype={ name : "xx", age : 18, add : function(){ console.log("第一句话。"); } } var stu1 = new xy(); stu1.add();

js中混合模式声明对象

对象的遍历

遍历对象的数组,对象可以当作数据处理 for in

var ren = { name: 'xy', age: 16, dome: function (param) { document.write('xxxxx'); } } for(var i in ren){ //i是属性或方法名称 console.log(ren[i]); //取得是属性的值,或者是方法的定义代码 }

使用构造函数声明的对象要实例化之后才可以进行遍历。

对象的储存

封装

把对象内部数据和操作细节进行隐藏。

//闭包案例 function a(){ i = 999; function b(){ //特权方法 return i; } return b; } c = a()(); console.log(c);

封装案例:

封装案例 js中处处是对象,面向对象的第一步当然就是封装了,由于Js中没有类的概念,所以封装起来也比较麻烦,下面介绍两种js的封装。

1、使用约定优先的原则,将所有的私有变量以_开头

/** * 使用约定优先的原则,把所有的私有变量都使用_开头 */ var Person = function (no, name, age) { this.setNo(no); this.setName(name); this.setAge(age); } Person.prototype = { constructor: Person, checkNo: function (no) { if (!no.constructor == "string" || no.length != 4) throw new Error("学号必须为4位"); }, setNo: function (no) { this.checkNo(no); this._no = no; }, getNo: function () { return this._no; }, setName: function (name) { this._name = name; }, getName: function () { return this._name; }, setAge: function (age) { this._age = age; }, getAge: function () { return this._age; }, toString: function () { return "no = " + this._no + " , name = " + this._name + " , age = " + this._age; } }; var p1 = new Person("0001", "鸿洋", "22"); console.log(p1.toString()); //no = 0001 , name = 鸿洋 , age = 22 p1.setNo("0003"); console.log(p1.toString()); //no = 0003 , name = 鸿洋 , age = 22 p1.no = "0004"; p1._no = "0004"; console.log(p1.toString()); //no = 0004 , name = 鸿洋 , age = 22

看完代码,是不是有种被坑的感觉,仅仅把所有的变量以_开头,其实还是可以直接访问的,这能叫封装么,当然了,说了是约定优先嘛,这种方式还是不错的,最起码成员变量的getter,setter方法都是prototype中,并非存在对象中,总体来说还是个不错的选择。如果你觉得,这不行,必须严格实现封装,那么看第二种方式。

2、严格实现封装

/** * 使用这种方式虽然可以严格实现封装,但是带来的问题是get和set方法都不能存储在prototype中,都是存储在对象中的 * 这样无形中就增加了开销 */ var Person = function (no, name, age) { var _no , _name, _age ; var checkNo = function (no) { if (!no.constructor == "string" || no.length != 4) throw new Error("学号必须为4位"); }; this.setNo = function (no) { checkNo(no); _no = no; }; this.getNo = function () { return _no; } this.setName = function (name) { _name = name; } this.getName = function () { return _name; } this.setAge = function (age) { _age = age; } this. getAge = function () { return _age; } this.setNo(no); this.setName(name); this.setAge(age); } Person.prototype = { constructor: Person, toString: function () { return "no = " + this.getNo() + " , name = " + this.getName() + " , age = " + this.getAge(); } } ; var p1 = new Person("0001", "鸿洋", "22"); console.log(p1.toString()); //no = 0001 , name = 鸿洋 , age = 22 p1.setNo("0003"); console.log(p1.toString()); //no = 0003 , name = 鸿洋 , age = 22 p1.no = "0004"; console.log(p1.toString()); //no = 0003 , name = 鸿洋 , age = 22

看上面的代码,去掉了this.属性名,严格的实现了封装,只能通过getter,setter访问成员变量了,但是存在一个问题,所有的方法都存在对象中,增加了内存的开销。

3、以闭包的方式封装

/** * 使用这种方式虽然可以严格实现封装,但是带来的问题是get和set方法都不能存储在prototype中,都是存储在对象中的 * 这样无形中就增加了开销 */ var Person = (function () { var checkNo = function (no) { if (!no.constructor == "string" || no.length != 4) throw new Error("学号必须为4位"); }; //共享变量 var times = 0; return function (no, name, age) { console.log(times++); // 0 ,1 , 2 var no , name , age; this.setNo = function (no) { checkNo(no); this._no = no; }; this.getNo = function () { return this._no; } this.setName = function (name) { this._name = name; } this.getName = function () { return this._name; } this.setAge = function (age) { this._age = age; } this. getAge = function () { return this._age; } this.setNo(no); this.setName(name); this.setAge(age); } })(); Person.prototype = { constructor: Person, toString: function () { return "no = " + this._no + " , name = " + this._name + " , age = " + this._age; } } ; var p1 = new Person("0001", "鸿洋", "22"); var p2 = new Person("0002", "abc", "23"); var p3 = new Person("0003", "aobama", "24"); console.log(p1.toString()); //no = 0001 , name = 鸿洋 , age = 22 console.log(p2.toString()); //no = 0002 , name = abc , age = 23 console.log(p3.toString()); //no = 0003 , name = aobama , age = 24

上述代码,js引擎加载完后,会直接执行Student = 立即执行函数,然后此函数返回了一个子函数,这个子函数才是new Student所调用的构造函数,又因为子函数中保持了对立即执行函数中checkNo(no) ,times的引用,(很明显的闭包)所以对于checkNo和times,是所有Student对象所共有的,创建3个对象后,times分别为0,1,2 。这种方式的好处是,可以使Student中需要复用的方法和属性做到私有且对象间共享。

原型继承

原型和原型链

原型:是利用prototype 添加属性和方法

原型链:JS在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做_proto_的内置属性,用于指向创建它的函数对象的原型对象prototype

通过原型和原型链引出继承的概念

实例: 完整案例:

原型继承:

示例:

构造函数的继承

在子类内部构造父类的对象实现继承 call:调用一个对象的一个方法,以另一个对象替换当前对象 apply:应用某一对象的一个方法,用另一个对象替换当前对象

关键词:

instanceofdelete、call、apply、arguments、callee、this

instanceof 判断变量是否是对象的实例

var arr = new Array(); alert(arr instanceof Array); //true alert(arr instanceof Object);

delete 删除对象的属性 (不能删除对象、变量、原型链中的属性)

function arr(){ this.name = "xy" } var obj = new arr(); console.log(obj.name); //xy delete obj.name; console.log(obj.name); //undefined

callee 返回正在执行的function对象

arguments

apply

对象冒充

将父类的属性和方法一起传给子类作为特权属性和特权方法

walk属性继承不到,其他的都可以。

最新回复(0)