04、JS面向对象
js面向对象的名词解析
属性:事物的特征
方法:事物的功能
对象:事物的实例
原型:js函数中由prototype属性引用了一个对象,即原型对象
js闭包
闭包:闭包是一个拥有许多变量和绑定这些变量的环境的表达式(通常是一个函数)。
闭包的优点:有利于封装,可以访问局部变量。
闭包的缺点:内存占用严重,容易产生内存泄漏。
声明对象的方法
字面式:
var person = { name : "ming", sex : "male", age : 18 }; console.log(person.name); //对象名.属性名 调用
Object方式:
//Object是所有对象的基类、根,所有的JavaScript对象都是由Object延伸的。 var person = new Object(); person.name = "ming"; person.sex = "male"; person.age = 18; console.log(person.name); //对象名.属性名 调用
构造方式:
function Person(name,sex,age){ this.name = name; //this.name指的是name属性。后面的name指的是参数。 this.sex = sex; this.age = age; } var per = new Person("ming","male",18); //使用前要先实例化对象 console.log(per.name); //实例化的名.属性名 调用
工厂方式:
function person (name,sex,age){ var ren = new Object(); ren.name = name; //ren.name指的是name属性。后面的name指的是参数。 ren.sex = sex; ren.age = age; return ren; //工厂方式必须返回对象 } var xx = person("ming","male",18); //使用前要先实例化对象 console.log(xx.name); //实例化的名.属性名 调用
构造方式和工厂方式的区别:
构造方式:不会显示创建对象,将属性赋值给this,不需要return对象。
工厂方式:在方法内部创建Object对象,返回Object对象,属性和方法都是赋给Object对象。
原型模式:
//任何js方法或函数,都自带一个prototype属性,且它以对象方式存在。
//原型模式根本:函数本身声明为空内容,利用prototype定义一些属性及方法。
function person(){ } person.prototype.name = "ming"; person.prototype.sex = "male"; person.prototype.age = 18; var ren = new person(); console.log(ren.name);
或 json数据定义属性和方法
function person(){ } person.prototype ={ name : "ming", sex : "male", age : 18 } var ren = new person(); console.log(ren.age);
混合模式:
//构造方式 + 原型模式 function person(name,sex,age){ this.name = name; this.sex = sex; this.age = age; } person.prototype = { address : "beijing", add : function(a,b){ return a+b; } } var ren = new person("ming","male",18); console.log(ren.name); console.log(ren.add(1,1));
对象的遍历
for in 方法(用什么方式声明的对象都可以遍历,有些需要先实例化)
如:
function person(){ } person.prototype.name = "ming"; person.prototype.sex = "male"; person.prototype.age = 18; var ren = new person(); // console.log(ren.name); for(var i in ren){ console.log(i); //i为属性名 console.log(ren[i]); //ren[i]为属性值 }
对象的存储
存储分为:栈内存(基本类型)、堆内存(引用类型)、代码段(应用类型里面的方法)、数据段
封装
封装:把对象内部数据和操作细节进行隐藏。(大多面向对象的语言都支持封装的特性,提供了private关键字来隐藏某些属性或方法,用来限制被封装的数据或者内容的访问,只对外提供一个对象的专门访问的接口。接口一般为调用方法)
注:JavaScript不支持封装的关键词,但是可以通过闭包实现封装。
原型链继承
原型:是利用prototype添加属性和方法
原型链:JS在创建对象(不论普通对象还是函数对象)的时候,都有一个叫做__proto__的内置属性,用于指向创建它的函数对象的原型对象prototype
function person(){ this.name = "小明"; } person.prototype.age = function(){ alert(18); }; var stu = new person; //1、var stu={} –> //2、stu.__proto__ = person.prototype –> //3、对象初始化 stu.age();//stu为变量,没有age方法, //会从stu.__proto__里面找,stu.__proto__等于person.prototype, //在person.prototype里面恰好找到了age方法
原型继承方法
1、定义父类型构造函数
2、给父类型的原型添加方法
3、定义子类型的构造函数
4、创建父类型的对象赋值给子类型的原型
5、将子类型原型的构造属性设置为子类型
6、给子类型原型添加方法
7、创建子类型的对象:可以调用父类型的方法
重点:子类型的原型为父类型的一个实例对象
// 1、定义父类型构造函数 function fulei(){ this.name = "小明"; } // 2、给父类型的原型添加方法 fulei.prototype.say = function(){ alert("父类"); } // 3、定义子类型的构造函数 function zilei(){ this.name ="小东"; } // 4、创建父类型的对象赋值给子类型的原型 zilei.prototype = new fulei(); // 5、将子类型原型的构造属性设置为子类型 zilei.prototype.constructor = zilei; // 6、给子类型原型添加方法 zilei.prototype.talk = function(){ alert("子类"); } // 7、创建子类型的对象:可以调用父类型的方法 var ssr = new zilei(); ssr.say(); //父类 // ssr.talk(); //子类 // alert(ssr.name); //小东
优点:
- 父类新增原型方法/原型属性,子类都能访问到
- 简单,易于实现
缺点:
- 要想为子类新增属性和方法,必须要在zilei.prototype = new fulei();这样的语句之后执行,不能放到构造器中
- 无法实现多继承
- 来自原型对象的所有属性被所有实例共享
- 创建子类实例时,无法向父类构造函数传参
构造函数继承
构造函数继承:在子类内部构造父类的对象实现继承。
构造函数继承方法
1、定义父类型构造函数
2、给父类型添加方法或属性
3、定义子类型构造函数
4、创建子类型属性为父类型本身
5、执行该子类型
6、给子类型添加其他方法或属性
7、实例化子类型:可以调用父类型的方法
function parents(){// 1、定义父类型构造函数 this.name = "zhangsan";// 2、给父类型添加方法或属性 this.age = 18; } function child(){// 3、定义子类型构造函数 this.ch = parents;// 4、创建子类型属性为父类型本身 this.ch();// 5、执行该子类型 this.abc = 134343;// 6、给子类型添加其他方法或属性 } var a = new child();// 7、实例化子类型:可以调用父类型的方法 alert(a.name); alert(a.abc);
call/apply继承方法(属于构造函数继承)
1、定义父类型构造函数
2、给父类型添加方法或属性
3、定义子类型构造函数
4、用call/apply方法,把当前对象的值传给父类型
5、定义子类型方法或属性
6、实例化子类型:可以调用父类型的方法
function parents(name,age){// 1、定义父类型构造函数 this.say = function(){// 2、给父类型添加方法或属性 alert("我是父亲"+name+"我今年"+age+"岁"); } } function child(name,age){// 3、定义子类型构造函数 // parents.call(this,name,age); // 4、用call/apply方法,把当前对象的值传给父类型 parents.apply(this,[name,age]); this.abc = 234343243;// 5、定义子类型方法或属性 } var b = new child("张三",35);// 6、实例化子类型:可以调用父类型的方法 b.say(); alert(b.abc);
面向对象关键词
instanceof:判断变量是否是对象的实例(a instanceof Object)
delete:删除对象属性,删除不了原型链的属性,删除不了变量(delete a.name)
call、apply:两个方法的作用一样,改变this的指向。call可以有多个参数;apply只能有两个参数,第二个参数必须是数组。(可以只带一个参数,一个参数表示this的指向)
callee:返回正在执行的function对象(function的内容)。(arguments.callee)。(callee是auguments的一个属性。arguments.callee表示function内容,auguments.callee()表示在函数内容里调用本函数。)
arguments:类数组(所有函数都有)。arguments.length表示参数的个数,还可以用数组下标表示参数内容arguments[i]
this:
1、可以在函数内部定义属性(function (){ this.x=123;alert(this.x); }() //里面的x是全局变量)
2、作为方法调用,构造函数内this指向当前对象(function person(){ this.name="zhang";this.age=18; })
3、call、apply的第一个参数,改变this的指向。
对象冒充
对象冒充:将父类的属性和方法一起传给子类作为特权属性和特权方法。
function person(name,age){ this.name = name; this.age = age; this.sayHi = function(){ alert("hi"); } } person.prototype.walk = function(){ alert("walk"); }
//混合的方式声明对象,上面构造是特权属性和方法,下面原型是共有属性和方法。
function student (name,age,grade){ this.newMethod = person; this.newMethod(name,age); this.grade = grade; } var s1 = new student("zhangsan",15,9); //s1继承了person的特权属性和方法,没有继承共有属性和方法。 // alert(s1.name); //继承了person的name // alert(s1.grade);//student自带的grade // s1.walk();//报错。共有属性和方法无法继承