面向对象
对代码的一种抽象,对外统一提供调用接口的编程思想
怎么来的:基于原型的面向对象中,对象则是依靠构造器利用原型构造出来的。
属性:事物的特性
方法:事物的功能
对象:事物的一个实例
原型: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
:应用某一对象的一个方法,用另一个对象替换当前对象
关键词:
instanceof、delete、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属性继承不到,其他的都可以。