概述
所有的函数都有prototype属性
function fn(){};
// fn.prototype ==> 内存地址 ==》存储一个对象
alert(fn.prototype instanceof Object)
闭包
使用闭包可以在全局作用域下访问局部变量
闭包的优缺点:
优点:可以访问局部变量,封装性好
缺点:使得局部变量一直在内存中,内存消耗大
function fn1(){
var a=1;
function fn2(){
// 在这里可以访问局部变量a
a++;
}
// 将fn2返回出去,也就是将a变量反出去
return fn2;
}
var fn = fn1() //fn2
fn() //执行了fn2,在作用域外修改了局部变量a
对象的声明
字面式声明
var person={
name:'zhangsan',
age: 26,
sex:'man',
eat:function(fds){
aleart('我在吃'+fds)
}
}
person.eat('面条')
new操作符后跟Object构造函数
var person=new Object();
person.name='zhangsan';
person.age=26;
person.sex='man';
person.eat=function(fds){
// this 指向当前对象
aleart(this.name + '在吃'+fds)
};
person.eat('面条')
js中构造方法声明对象
function Person(name,age,sex){
this.name=name;
this.age=age;
this.sex=sex;
this.eat=function(str){
alert(this.name+'在吃'+str)
}
}
// this指向当前实例化对象
// this指向person1
var person1=new Person('xm',26,'nan');
person1.eat('面条');
// this指向person2
var person2=new Person('xh',26,'wonan');
person1.eat('饺子')
js中工厂方式声明对象
function createObject(name,age,sex){
var obj=new Object();
obj.name=name;
obj.age=age;
obj.sex=sex;
obj.show=function(){
console.log(this.name+this.age+this.sex)
}
return obj;
}
// 工厂模式在使用的时候不需要new
var p1=createObject('xh',26,'nan');
var p2=createObject('xm',28,'nan');
构造方式和工厂模式的不同:
- 构造方式不会显示创建对象,将属性都赋值给了this,不需要return对象;
- 工厂模式必须在方法内部创建object对象,并返回它,属性和方法都是赋值给object对象
js中原型模式声明对象
任何js方法或函数,都自带一个prototype属性,且它以对象方式存在。
function person(){}
person.prototype.name='zhangsan';
person.prototype.age=26;
person.prototype.sex='男';
person.prototype.show=function(){
console.log(this.name+this.sex+this.age)
}
var p1=new person();
// 还可以以json数据定义属性和方法
person.prototype={
name:'zhangsan',
age:26,
sex:'男',
show:function(){
console.log(this.name+this.sex+this.age)
}
}
js中混合模式声明对象: 构造+原型
function person(name,age,sex){
this.name=name;
this.age = age;
this.sex = sex;
}
person.prototype.show=function(){
console.log(this.name+this.sex+this.age)
}
var p1=new person('xh',18,'nan');
对象遍历及存储
遍历对象的属性及方法
var ren = {};
ren.name='xh';
ren.age=18;
ren.sex='男‘;
ren.demo=function(){
document.write('aaa');
}
// 遍历
for(var key in ren){
aleart(ren[key])
}
对象在内存中的分布
封装
- 把对象内部数据和操作细节隐藏起来;
- js中可以通过闭包实现封装
原型和原型链
原型:是利用prototype添加属性和方法
原型链:JS在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做__proto__的内置属性,用于指向创建它的函数对象的原型对象prototype
var person=function(){};
var p = new person();
// new实例化对象经历了以下三部
// 1.创建对象:var p={};
// 2.将实例的原型链指向构造函数的prototpye原型:p.__proto__=person.prototype
// 3.初始化对象:p==》person.call(p)
原型链实现过程
var fn=new fn2();
上面的一句话实现了: fn.proto=fn2.prototypefn2.prototype=new fn1();
上面一句话实现了:fn2.prototype=fn1.prototype
然后 fn.proto=fn2.prototype=fn1.prototype
fn也就有了fn1和fn2原型上的属性和方法
var fn1=function(){};
fn1.prototype.demo1=function(){
alert('我是fn1'};
}
fn1.prototype.a=500;
var fn2=function(){};
fn2.prototype=new fn1();
fn2.prototype.demo2=function(){
alert('我是fn2'};
}
fn2.prototype.a=1000;
var fn=new fn2();
fn.demo2();
fn.demo1();
fn.a //1000 子类上的属性会将父类上的属性覆盖掉
原型继承
function Person(name,age,sex){
this.name=name;
this.age=age;
this.sex=sex;
};
Person.prototype.say=function(){
aleart('你好')
}
function Student(){};
// 原型继承
Student.prototype=new Person();
Student.prototype.gran=function(){
print('年纪')
}
var s=new Student();
s.say();
// 过程解析:
// 1. 现在s上找say方法,s实例上没有就在s.__proto__属性上找,该属性指向的是Student.prototype原型
// 2. Student.prototype原型原型等于new Person(),也就是将__proto__属性指向了Person.prototype,然后在Person.prototype上找到了say方法;
构造函数的继承
function Parent(name){
this.name=name;
this.say=function(){
console.log('我是父类')
}
}
function Child(name,age){
//继承父类
this.pObj=Parent;
// 给父类传参数
this.pObj(name);
this.age=age;
this.sayC=function(){
console.log('我是子类')
}
}
var p=new Parent('xh');
p.say();
var c=new Child('xm',18);
c.sayC()
- call、apply实现继承
function Person(name,age,sex){
this.name=name;
this.age=age;
this.sex=sex;
this.show=function(){
aleart(this.name+':'+this.age+':'+this.sex)
}
}
// call实现继承
function Student(name,age){
// this指向Student
Person.call(this,name,age)
}
// apply实现继承
function Teacher(name,age,sex){
Person.apply(this,[name,age,sex])
}
var p=new Person('人类',18,'男');
p.show();
var s=new Student('学生',18);
s.show();
var t=new Teacher('老师',28,'女')
t.show()
js面向对象的关键词
instanceof
判断变量是否是对象的实例
function test(){};
var fn=new test();
// 判断fn是否是test的实例
console.log(fn instanceof test) //true
console.log(fn instanceof Object) //true
delete
删除对象的属性,不可以删除方法;
删除不了变量和原型链上的属性;
function fun(){
this.name='xh';
this.say=function(){
alert(this.name)
}
}
var fn=new fun();
fn.say();
delete fn.name;
fn.say();
call,apply
function add(a,b){
alert(a+b);
}
function sub(a,b){
alert(a-b);
}
// add 替换 括号中的sub,所以执行a+b
// 括号中的sub位置必须指向存在的对象
add.call(sub,5,3) //5+3=8
function animal(){
this.name='animal';
this.showName=function(){
alert(this.name);
}
}
function cat(){
this.name='cat';
}
var an=new animal();
var c=new cat();
// an.showName.call(c,'');
an.showName.apply(c,[]);
arguments
- arguments.length
- arguments[index]
callee
- 返回正在执行的function对象,function内容;
- 它是arguments的属性:arguments.callee;
- 默认值:正在执行的function对象
- callee就是指代函数本身
function demo(){
// 弹出整个函数本身
alert(arguments.callee);
// 报错:循环执行本函数,造成死循环
alert(arguments.callee());
}
// 可以使用callee进行回调
function sum(n){
if(n<=1) return;
return n+arguments.callee(n-1)
}
alert(sum(5))
this
- 可以在函数内部定义属性/变量
function test(){
this.x=1; //this 全局变量 等同于x=1
}
- 作为方法调用 构造函数内this指当前实例对象
function Test(){
this.name='xh';
this.age=18;
}
// this指代当前实例t
var t=new Test();
t,name
- 在apply、call中
var x=0;
function test(){
alert(this.x);
}
var o={};
o.x=1;
o.m=test;
// apply中没有传递参数,默认指向window,所以弹出全部变量x的值
o.m.apply(); //0
// 指向o
o.m.apply(o); //1
对象冒充
将父类的属性和方法一起传给子类作为特权属性和特权方法
function paren(name,age){
this.name=name;
this.age=age;
this.say=function(){
alert('说话')
}
}
parent.prototype.walk=function(){
alert('走了吧')
}
function student(name,age,sex){
this.obj=paren; //冒充paren
this.obj(name,age);
this.sex=sex;
}
var s=new student('xh',18,'男');
s.say(); //继承了
s.walk(); //报错
// s继承了paren中的特权属性和方法,没有继承共有属性和方法