面向对象的思想
面向对象编程:见搜狗:面向对象(mianxiangduixiang) js关键字(jsguanjianzi) apply call
一切事物皆对象
以下使用人和汽车进行解析对象要要素:
1,人的属性:
2,人的方法:
3,人的对象:
4,原型:prototype属性指向一个内存地址----该地址存放着一个原始对象。
prototype就是该对象的引用。 即:原型对象。
汽车的对象解析四要素:
1,汽车的属性
2,汽车的方法:
3,汽车的实例:
众多汽车中的一辆车
4,原型:prototype属性指向一个内存地址----该地址存放着一个原始对象。prototype就是该对象的引用。 即:原型对象。
<script>
function f(){
}
console.log(f.prototype);// 返回 Object
// a instance of Object 引用数据类型 判断 a 是否是 Object的实例
// a typeof b 基本数据类型 判断 a 是否是 b的实例
console.log(f instanceof Object); // 返回 true
</script>
继承方式分为4种:
继承分为:原型继承,构造函数继承,call,apply 四种方式
1,原型继承
<script type="text/javascript">
//原型:是利用prototype添加属性和方法
//原型链:JS在创建对象(不论是普通对象还是函数对象)的时候,
//都有一个__proto__的内置属性,用于指向创建它的函数对象的原型对象prototype
//原型的值可以是一个对象,也可以是null Object.__proto__指向 null 。
//js对象有一个__proto__属性,指向它的构造函数的prototype属性
//js中所有的函数都有一个prototype属性,该属性引用了一个对象,即原型对象,也简称原型
/*__proto__ 和 prototype 的区别:
1,__proto__ 是每个对象都有的属性。不是一个规范的属性,火狐有点不同。
2,prototype 是每个函数都有的属性
在多数情况下,__proto__ === constructor.prototype 返回 true
Object.create()创建的对象不适用此等式*/
//js继承:
//原型:使用prototype对象来添加属性和方法
var person = function(){};//创建一个空方法
var p = new person();
// 实例化p对象,经历三个阶段:
// 1,var p={} 实例化一个p对象
// 2,p.__proto__ = person.prototype,出于原型链含义, __proto__是任何对象都自带的属性。
// JS在创建对象(不论是普通对象还是函数对象)的时候,
//都有一个__proto__的内置属性,用于指向创建它的函数对象的原型对象prototype
// 3,创建对象(初始化), p ==> person.call(p)
/*console.log(person.prototype);//{constructor: ƒ} 构造函数
console.log(p.__proto__);//{constructor: ƒ} 构造函数
console.log(person.prototype instanceof Object);//返回true
console.log(p.__proto__ instanceof Object);//返回true
console.log(person.prototype === p.__proto__);//返回true*/
/////---------
function pText(){
this.say=function(){
alert("111");
}
}
pText.prototype.play=function(){
alert("22");
}
pText.prototype.name="haha";
var cText=function(){
this.eat=function(){
alert("33");
}
};
cText.prototype.name="lala";
cText.prototype=new pText();
var person=new cText();
//注意点:返回的不是 lala,而是 haha,这里先执行name="lala",
//再继承父元素中的name="haha",所以后面覆盖前面自定义的name。
console.log(person.name);//返回 haha
person.eat();//33
person.say();//11
person.play();//22
</script>
//-----------
<script>
//prototype是原型(理解为基本框架), __proto__是原型链
var a={};//a是一个对象,创建a的构造函数:var a=new Object();
//根据原型链的定义:js创建任何对象,都内置一个__proto__属性,
//指向创造它的函数对象的原型对象prototype,即:a.__proto__===Object.prototype.
console.log(a.__proto__);//页面第一次加载控制台显示:Object,刷新就不是了
console.log(a.__proto__===Object.prototype);//返回 true
console.log(a.__proto__===a.constructor.prototype);//返回 true
console.log(a.prototype);//返回 undefined 未定义,函数才有prototype属性
var b=function(){};
var j=new b();
console.log(j.__proto__);//页面第一次加载控制台显示:Object,刷新就不是了
console.log(b.prototype);//页面第一次加载控制台显示:Object,刷新就不是了
////根据原型链的定义:js创建任何对象,都内置一个__proto__属性,
//指向创造它的函数对象的原型对象prototype,即:f.__proto__===b.prototype.
console.log(j.__proto__===b.prototype);//返回 true
var A = function(){};
var a = new A();
// a.say();//不存在say方法会报错的
// 原型链:a属性中查找---》 a.__proto__ ---》 Object.__proto__===null,不存在就报错。
// 这就是一条围绕__proto__的原型链。
// 原型链的顶层就是Object.prototype,而这个对象的是没有原型对象的。
/*原型链解析:a.say();
1,首先会在a内部属性中查找 say方法。
2,没找到,则会在 a.__proto__原型对象 中查找 say方法
3,没找到,则会在 Object.__proto__原型对象 中查找 say方法,因为没找到,且Object.__proto__===null,所以
a.say()就会报错。*/
console.log(a.__proto__); //A {}(即构造器function A 的原型对象)
console.log(a.__proto__.__proto__); //Object {}(即构造器function Object 的原型对象)
//原型链的顶层就是Object.prototype===null
console.log(a.__proto__.__proto__.__proto__); //null
//再继续往下就是语法错误
console.log(a.__proto__.__proto__.__proto__.__proto__); //报错,不存在该
//---------
////分析:原型链的执行过程
var person = function(){} //父方法
person.prototype={
"name":"zs",
"age":"18",
"address":"China",
"say":function(){
alert("今天天气特别好!");
}
}
// var p=new person();
// p.say(); 原型链解析:
//1,首先到p对象中查找是否有say方法。
//2,p.__proto__ === person.prototype ,
// 所以直接到person.prototype中查找是否有say方法。
//3,最后到Object.prototype中去查找,Object.prototype=null.
var student=function(){} // 子方法
//prototype是原型对象,可以随便赋值。
//p.__proto__是原型链,其赋值对象只能是person.prototype
student.prototype=new person();//解析为:创建student的实例原型为:person。
//上面一行已经定义了student.prototype,这里就不能再定义了
//否则就会覆盖上面的内容。
//必须注释掉,否则会覆盖上面student.prototype的定义
//student.prototype={
// name:"zheng",
// age:18,
// address:"China",
// hello:function(){
// alert("今天天气真好!");
// }
// }
student.prototype.hello=function(){
alert("您好啊");
}
student.prototype.name="覆盖前面的name";
var stu=new student();//实例化student对象
// stu.say();
//原型链解析:都是在__proto__原型链上进行解析的,stu.__proto__ === student.prototype
//1,首先在stu本身查看是否有say方法。
//2,再到stu.__proto__中查看是否有say方法。
//3,因 stu=new student(); 所以,stu.__proto__ 指向 student.prototype。
//4,因 student.prototype=new person();
// 所以,stu.__proto__ 等于直接指向 person.prototype
//5,直接在person.__proto__中查找say方法
//6,最后 var person = function(){} 是Object(){}这个函数实例化而来的,如:
// var person=new Object();,
// 所以,person.__proto__ 指向 object.prototype=null
//执行stu.say()时查找say方法的过程:注意查找__proto__对象就等于查找prototype对象。
//stu-->stu.__proto__-->person.__proto__-->obj.__proto__
//原型链,可以使用作用域链的模式来理解:
//1,子对象的属性是由内向外进行查找,存在多个值,取最近的值。如果不存在,报错或者undefined
console.log(stu.address);//返回 China
//子对象会继承父对象的属性,且是由内向外进行查找,存在多个值,取最近的值。如果不存在,报错或者undefined
console.log(stu.name);//返回:覆盖前面的name
stu.hello();//返回 您好啊
console.log(person.name);//name是函数的关键字,返回函数名称。
console.log(person.age);//没有实例化person,所以返回 undefined
var p=new person();//实例化person对象
console.log(p.name);//返回 zs
//---------
//分析:原型链的执行过程
function Person(name,age,address){
this.name=name;
this.age=age;
this.address=address;
}
Person.prototype.hello=function(){
return "大家好,我叫:"+this.name+",今年"+this.age+"岁了。家住在:"+this.address;
}
function Student(grade,sex){
this.grade=grade;
this.sex=sex;
}
//谨记:指定prototype原型时,一定要为其父对象以及实际参数。
//说白了就是以某某为模板(基础)作为创建对象的标准。
Student.prototype=new Person("zs",18,"清源乡");
Student.prototype.showInfo=function(){
return "我读"+this.grade+"年纪了,我的性别是:"+this.sex;
}
//Person {name: undefined, age: undefined, address: undefined},没有跟参数,默认undefined
// console.log(new Person());
var stu=new Student(3,"male");
//以下是stu.__proto__本身原型链对象的属性
console.log(stu.grade);//3
console.log(stu.sex);//male
console.log(stu.showInfo());//我读3年纪了,我的性别是:male
//以下是stu父对象person.__peroto__的原型链对象的属性
console.log(stu.name);//zs
console.log(stu.age);//18
console.log(stu.hello());//大家好,我叫:zs,今年18岁了。家住在:清源乡
//分析stu.showInfo()原型链的执行过程:
//1,执行stu.showInfo()时
// 首先会在stu中查找showInfo方法
// 再到stu.__proto__中查找showInfo方法
//2,因 var stu=new Student(3,"male"); 实例化Student对象
// 所以 stu.__proto__=Student.prototype。
//3,因 Student.prototype=new Person("zs",18,"清源乡");
// 所以 stu.__proto__ = Student.prototype = Person.prototype
// 即:会在Person.__proto__中查找showInfo方法
//4,如果还没找到showInfo方法,那么会执行到原型链顶层object.prototype,
// 原型链的顶层就是Object.prototype,而这个对象的是没有原型对象的。
//综上所述:原型链就是基于__proto__查找指定属性的过程。
//上诉原型链为:
/* stu-->stu.__proto__-->(Student.prototype).__proto__-->
(Person.prototype).__proto__-->Object.prototype */
</script>
<!-- stu.name取值:在原型链中就近原则取值 -->
<script type="text/javascript">
function person(){
this.say=function(){
alert("say");
}
this.name="1111";
}
//等价于在person里面声明 this.name="1111";
// person.prototype.name="1111";
var student=function(){
this.name="zheng";
this.age=18;
/*this.hello=function(){
console.log("hello");
}*/
} // 子方法
student.prototype.address="china";
student.prototype.hello=function(){
console.log("hello");
}
student.prototype.name="2222";
//以下三行代码一起解析:创建student实例的原型为:person,
//所有的属性和方法都以person函数里面的定义为标准
//按照程序从上到下执行,查找stu.name的值。
//1,到stu本身查找。
//this.name="zheng"; 如果没有定义则继续向外查找
//2,stu.__proto__ 指向 student.prototype 因student.prototype=new person();
//3,所以 stu.__proto__ 直接指向 person.prototype
//person.prototype.name="1111";等价于this.name="1111";如果没有定义就报错或者未定义undefined.
//4,即:直接在(person.prototype).__proto__ 中查找
//5,最后到原型链最顶层Object.prototype为空:null.
//上面的student.prototype.name="2222"; 这句话是没有执行的,所以stu.name不可能为2222.
//当,如果两行代码的执行先后顺序改变一下,那么stu.name的值就一定会是"2222"。因为给重定义了,如下:先执行 1 再执行 2
//1,student.prototype=new person();
//2,student.prototype.name="2222";
student.prototype=new person();
var stu=new student();//实例化student对象
console.log(stu.name);//返回: 1111
</script>
///--------------------
<!-- 构造函数继承 -->
什么是构造函数:就是在函数里面使用 this 关键词来定义属性及其方法
在子类内部构造父类对象来实现继承:
child.ob=parent;//把父类对象赋值给子类中的一个属性
child.ob(name,age,sex);//参数:需要继承父类中的属性,需要继承几个些几个
<script>
//父类中的参数一定小于等于子类参数,子类参数即属性一定是大于等于父类属性
function People(name,age,sex,height){
this.name=name;
this.age=age;
this.sex=sex;
this.height=height;
this.say=function(){
console.log("我的名字叫:"+this.name+",身高为:"+this.height+"年龄:"+this.age+",this.sex:"+this.sex);
}
}
function Student(name,age,height,grade,num){
this.obj=People;//在子类中构造一个父类对象,赋值给子类的一个属性
//这里只继承People中的name,age,其中sex没有继承,也没有写该值,默认值为undefined
this.obj(name,age);//需要继承几个属性就写几个属性的参数
this.height=height;
this.grade=grade;
this.num=num;
this.hello=function(){
//父类中的sex属性没有继承,所以this.sex=undefined
//下面的this.name this.age的值是从父类中继承过来的,其他都是子类自定义的。
console.log("name:"+this.name+",age:"+this.age+",sex:"+this.sex+",height:"+this.height+",grade:"+this.grade+",num:"+this.num);
}
}
var pe=new People("zs",28,"male",170);
console.log(pe.height); //返回 170
pe.say();//我的名字叫:zs,身高为:170年龄:28,this.sex:male
var stu=new Student("zheng",30,"male",180,"博士",88);
console.log(stu.height);//返回 180
console.log(stu.grade);//返回 博士
console.log(stu.num);//返回 88
stu.say();//返回:我的名字叫:zheng,身高为:male年龄:30,性别:undefined sex未定义
stu.hello();//返回 name:zheng,age:30,sex:undefined,height:male,grade:180,num:博士
</script>
<!-- 使用多种方式进行构造函数继承 -->
<script>
function Animal(name,count){
this.name=name;
this.count=count;
this.getInfo=function(){
console.log("动物名称:"+this.name+",数量:"+this.count);
}
}
//第一种方式继承父元素的所有属性及其方法:
function Cat(name,count){
this.obj=Animal;//普通方式继承
this.obj(name,count);
}
//第二种方式继承父元素的所有属性及其方法:
function Dog(name,count){
Animal.call(this,name,count);//使用call间接方式继承
}
//第三种方式继承父元素的所有属性及其方法:
function Pig(name,count){
Animal.apply(this,[name,count]);//使用apply间接方式继承
}
var an=new Animal("猴子",88);
var cat=new Cat("猫",99);
var dog=new Dog("狗",100);
var pig=new Pig("猪",200);
an.getInfo();
cat.getInfo();
dog.getInfo();
pig.getInfo();
</script>
//-------------------------
js中的关键词
包括:instanceof delete call apply arguments callee this
所有对象的本质都是Object对象
delete 只能删除对象的属性,对于其他一概没用。原型链的属性及方法无效。
callee是arguments的一个属性 指函数本省
arguments存在于每个函数中,详见:arguments搜狗
1,instanceof
// a instanceof b 检测a对象是否是b的实例,某种程度上理解是原型proto
function text(){};
var t=new text();
console.log(t instanceof text);//返回 true 解析: t 是 text对象的实例
var s=text;
console.log(typeof s);//返回 function
console.log(typeof text);//返回 function
console.log(typeof 1);//number
var str="woaini";
console.log(typeof str);//string
console.log(s === text);//true
console.log(s instanceof text);//false
console.log(s instanceof Object);//true Object首字母大写才表示js中的全局对象,否则object只是一个变量
2,delete 删除对象的属性,对于删除对象的方法,变量,以及原型链中的属性都是无效的。
总归一句话:delete只是用来删除对象的属性,其他一概不管。
function text(name){
this.name=name;
this.say=function(){
console.log(this.name);
}
}
var t=new text("zheng");
// console.log(t.name);//返回 zheng
// delete t.name;//删除 t的属性name
// console.log(t.name);//返回 undefined
t.say();//返回:zheng , delete只用于删除属性,对方法无效。并且都会执行say里面代码
delete t.say();//返回:zheng , delete只用于删除属性,对方法无效。并且都会执行say里面代码
t.say();//返回:zheng , delete只用于删除属性,对方法无效。并且都会执行say里面代码
//无法删除变量。无法删除原型链中的属性和变量
var a="love";
console.log(a);//返回 love
delete a;
console.log(a);//返回 love
3,call apply
<script>
function add(a,b){
console.log(a+b);
}
function substract(a,b){
console.log(a-b);
}
// a.fn.call(b,x1,x2,x3...) == a.fn.apply(b,[x1,x2,x3...])
// 表示使用b调用a中的fn的方法,跟的参数是x1,x2,x3...
/*add.call(substract,10,3);//返回 13
add.apply(substract,[10,3]);//返回 13
add.call(substract,10,3,4,6,7);//返回 13。多余参数直接忽略,不会报错。*/
function Animal(name){
this.name=name;
this.getInfo=function(){
console.log(this.name);
}
}
function Cat(name){
this.name=name;
}
var ani=new Animal("pig");
var cat=new Cat("cat");
//使用cat对象调用ani对象的getInfo方法,即说白了就是:
/*cat.getInfo=function(){
console.log(this.name);
}*/
ani.getInfo.call(cat);//返回 cat
ani.getInfo.apply(cat);//返回 cat
、、、、
//间接调用:
//1,对象没有call和apply方法,只有函数有
//2,apply可以将数组和类数组一次性的传递进函数中,call只能一个一个的传
//解析:call apply 两个都是调用其他对象的方法。
//唯一不同的是参数:
//obj.call(otherObj,a,b,c,d...); call的参数是一个一个传
//obj.apply(otherObj,[]); apply的参数是数组
//这里的obj可以是[],{}对象,也可以是全局作用对象window 或者某个局部作用域对象
var name="xm";
var person={};
person.name="xh";
person.getName=function(){
return this.name;
}
console.log(person.getName());//输出:xh
//这里的window用于改变person.getName里面的this变成:window,即返回window.name的值
console.log(person.getName.call(window));//输出:xm
//这里的window用于改变person.getName里面的this变成:window,即返回window.name的值
console.log(person.getName.apply(window));//输出:xm
//解析:person.getName.apply(window),使用window借用person的getName方法:
// window.getName=function(){
// return this.name;
// }
// window.name="xm";
var arr=[2,5];
function add(a,b){
return a+b;
}
//直接调用
console.log(add(3,6));
//使用了间接调用,call(指定作用域对象,argument1,argumen2...)
console.log(add.call(window,2,5));
//这里间接调用了window下面的arr的数组值, apply(指定作用域对象,数组)
console.log(add.apply(window,arr));
// 解析:add.apply(window,arr); 使用window借用add的方法,这里add本来前面有一个window。因为add方法本来就是一个全局变量。
// window.add(2,5){
// return 2+5;
// }
</script>
两个数组使用concat和apply进行合并
<script>
var arr1=[1,2,3,4];
var arr2=["a","b","c","d"];
alert(arr1.concat.apply(arr2,arr1));
alert(arr2.concat.apply(arr1,arr2));
</script>
4,arugments callee
<script>
function demo(){
console.log(arguments.callee);//返回dome函数体本身。
//console.log(arguments.callee());//报错,最大堆栈内存溢出,即死循环了
}
demo();
function sum(i){
if (i<=1) {
return i=1;
}else{
// return i+sum(i-1);等价于下面一行
return i + arguments.callee(i-1);
}
}
console.log(sum(5));
</script>
5,arguments 只有函数才有arguments属性
arugments.length 参数长度
arguments[1] 第二个参数
arguemnts 使用 for循环遍历
<script>
function len(){
console.log(arguments.length);//返回 参数长度
/*for(var p in arguments){//遍历所有参数
console.log(p+":"+arguments[p]);//这里表示的是索引,arguments[p]索引对应的值
}*/
//上下两种遍历方法都可以
for (var i = 0; i < arguments.length; i++) {
console.log(i+":"+arguments[i]);
}
console.log(arguments[2]);//输出 c
}
len("a","b","c","d");
</script>
6,this
a,如果text作为对象在下文实例化,那么this就是实例化后对象本身,
b,如果当作方法,没有实例化,那么this就window对象
<script>
function text(){
this.x=1;//就是函数的属性
//如果text作为对象在下文实例化,那么this就是实例化后对象本身,如果当做方法,没有实例化,那么this就window对象
console.log(this);//这里的this就是下面实例化的对象 t
}
text();
var t=new text();//实例化对象,方法text()中的this就是 t 对象
console.log(t.x);//输出 1
//----
function text1(){//没有实例化,则this就是window对象
this.x=1;
console.log(this.x);//没有实例化,则this就是window对象
}
text1();//输出 1 //没有实例化,则this就是window对象
/*var x=1;
function xx(){//xx本省属于全局变量window.xx
this.x=0;//等价于把全局变量 window.x=0;
console.log(this);//window对象
}
xx();//执行函数 修改 x=0;
console.log(x);//输出 0*/
</script>
///call apply jianjiediaoyong(间接调用)
<!-- 在call apply 情况的this
this指向的是call apply中的第一个参数
window.a.call/apply(b,x1,x2...),表示b借用window下面的a方法,
这里借用的都是call/apply前面的那个方法。 -->
<script>
var x=0;
function text(){
console.log(this.x);
console.log(this);
}
var o={};
o.x=1;
o.m=text;
//这里的this是window,其实下面的o是全局变量,即:window.o
o.m.apply();//输出:0 //window.o.m.apply()===>window.x
//window.o.m.apply(o),表示使用o对象借用window下面的o的m方法,
//这里的this是o对象,而不是window,window被o取代了。即调用的是o.x=1
o.m.apply(o);//输出:1 //即:使用o对象这个作用域代替window.o.m.apply()
</script>
<!-- 对象冒充 duixiangmaochong -->
<script>
function person(name,age){
this.name=name;
this.age=age;
this.say=function(){
console.log("name:"+this.name+",age:"+this.age);
}
}
person.prototype.walk=function(){
console.log("walk....");
}
function student(name,age,sex){
this.obj=person;//冒充person,传递特权属性和特权方法给子类
this.obj(name,age);//传递特权属性和特权方法给子类
this.sex=sex;
this.hello=function(){
console.log("name:"+this.name+",age:"+this.age+",sex:"+this.age);
}
}
//stu是student实例对象,继承了person的所有属性和方法
var stu=new student("zs",21,"male");
console.log(stu.name);//zs
console.log(stu.say);//say函数体
console.log(stu.walk);//输出undefined,如果执行stu.walk()方法,就会报错,因为不存在walk方法。
//注意,stu只能继承person中的特权属性和特权方法,不能继承原型对象中的方法和属性。
//所以上面stu.walk()是无法调用到,会报错,stu.walk is undefined
</script>
1,原型继承
<script type="text/javascript"> //原型:是利用prototype添加属性和方法//原型链:JS在创建对象(不论是普通对象还是函数对象)的时候,//都有一个__proto__的内置属性,用于指向创建它的函数对象的原型对象prototype//原型的值可以是一个对象,也可以是null Object.__proto__指向 null 。//js对象有一个__proto__属性,指向它的构造函数的prototype属性//js中所有的函数都有一个prototype属性,该属性引用了一个对象,即原型对象,也简称原型/*__proto__ 和 prototype 的区别:1,__proto__ 是每个对象都有的属性。不是一个规范的属性,火狐有点不同。2,prototype 是每个函数都有的属性在多数情况下,__proto__ === constructor.prototype 返回 trueObject.create()创建的对象不适用此等式*/
//js继承://原型:使用prototype对象来添加属性和方法var person = function(){};//创建一个空方法var p = new person();// 实例化p对象,经历三个阶段:// 1,var p={} 实例化一个p对象// 2,p.__proto__ = person.prototype,出于原型链含义, __proto__是任何对象都自带的属性。// JS在创建对象(不论是普通对象还是函数对象)的时候,//都有一个__proto__的内置属性,用于指向创建它的函数对象的原型对象prototype// 3,创建对象(初始化), p ==> person.call(p)/*console.log(person.prototype);//{constructor: ƒ} 构造函数console.log(p.__proto__);//{constructor: ƒ} 构造函数console.log(person.prototype instanceof Object);//返回trueconsole.log(p.__proto__ instanceof Object);//返回trueconsole.log(person.prototype === p.__proto__);//返回true*//////---------function pText(){this.say=function(){alert("111");}}pText.prototype.play=function(){alert("22");}pText.prototype.name="haha";var cText=function(){this.eat=function(){alert("33");}};cText.prototype.name="lala";cText.prototype=new pText();var person=new cText();//注意点:返回的不是 lala,而是 haha,这里先执行name="lala",//再继承父元素中的name="haha",所以后面覆盖前面自定义的name。console.log(person.name);//返回 hahaperson.eat();//33person.say();//11person.play();//22</script>//-----------<script>//prototype是原型(理解为基本框架), __proto__是原型链var a={};//a是一个对象,创建a的构造函数:var a=new Object();//根据原型链的定义:js创建任何对象,都内置一个__proto__属性,//指向创造它的函数对象的原型对象prototype,即:a.__proto__===Object.prototype.console.log(a.__proto__);//页面第一次加载控制台显示:Object,刷新就不是了console.log(a.__proto__===Object.prototype);//返回 trueconsole.log(a.__proto__===a.constructor.prototype);//返回 trueconsole.log(a.prototype);//返回 undefined 未定义,函数才有prototype属性
var b=function(){};var j=new b();console.log(j.__proto__);//页面第一次加载控制台显示:Object,刷新就不是了console.log(b.prototype);//页面第一次加载控制台显示:Object,刷新就不是了////根据原型链的定义:js创建任何对象,都内置一个__proto__属性,//指向创造它的函数对象的原型对象prototype,即:f.__proto__===b.prototype.console.log(j.__proto__===b.prototype);//返回 true
var A = function(){};var a = new A();// a.say();//不存在say方法会报错的// 原型链:a属性中查找---》 a.__proto__ ---》 Object.__proto__===null,不存在就报错。// 这就是一条围绕__proto__的原型链。// 原型链的顶层就是Object.prototype,而这个对象的是没有原型对象的。/*原型链解析:a.say();1,首先会在a内部属性中查找 say方法。2,没找到,则会在 a.__proto__原型对象 中查找 say方法 3,没找到,则会在 Object.__proto__原型对象 中查找 say方法,因为没找到,且Object.__proto__===null,所以a.say()就会报错。*/console.log(a.__proto__); //A {}(即构造器function A 的原型对象)console.log(a.__proto__.__proto__); //Object {}(即构造器function Object 的原型对象)//原型链的顶层就是Object.prototype===nullconsole.log(a.__proto__.__proto__.__proto__); //null //再继续往下就是语法错误console.log(a.__proto__.__proto__.__proto__.__proto__); //报错,不存在该//---------////分析:原型链的执行过程var person = function(){} //父方法person.prototype={"name":"zs","age":"18","address":"China","say":function(){alert("今天天气特别好!");}}// var p=new person();// p.say(); 原型链解析://1,首先到p对象中查找是否有say方法。//2,p.__proto__ === person.prototype ,// 所以直接到person.prototype中查找是否有say方法。//3,最后到Object.prototype中去查找,Object.prototype=null.
var student=function(){} // 子方法//prototype是原型对象,可以随便赋值。//p.__proto__是原型链,其赋值对象只能是person.prototypestudent.prototype=new person();//解析为:创建student的实例原型为:person。//上面一行已经定义了student.prototype,这里就不能再定义了//否则就会覆盖上面的内容。//必须注释掉,否则会覆盖上面student.prototype的定义 //student.prototype={// name:"zheng",// age:18,// address:"China",// hello:function(){// alert("今天天气真好!");// }// }student.prototype.hello=function(){alert("您好啊");}student.prototype.name="覆盖前面的name";var stu=new student();//实例化student对象// stu.say();//原型链解析:都是在__proto__原型链上进行解析的,stu.__proto__ === student.prototype//1,首先在stu本身查看是否有say方法。//2,再到stu.__proto__中查看是否有say方法。//3,因 stu=new student(); 所以,stu.__proto__ 指向 student.prototype。//4,因 student.prototype=new person();// 所以,stu.__proto__ 等于直接指向 person.prototype//5,直接在person.__proto__中查找say方法//6,最后 var person = function(){} 是Object(){}这个函数实例化而来的,如:// var person=new Object();,// 所以,person.__proto__ 指向 object.prototype=null//执行stu.say()时查找say方法的过程:注意查找__proto__对象就等于查找prototype对象。//stu-->stu.__proto__-->person.__proto__-->obj.__proto__//原型链,可以使用作用域链的模式来理解://1,子对象的属性是由内向外进行查找,存在多个值,取最近的值。如果不存在,报错或者undefinedconsole.log(stu.address);//返回 China//子对象会继承父对象的属性,且是由内向外进行查找,存在多个值,取最近的值。如果不存在,报错或者undefinedconsole.log(stu.name);//返回:覆盖前面的namestu.hello();//返回 您好啊console.log(person.name);//name是函数的关键字,返回函数名称。console.log(person.age);//没有实例化person,所以返回 undefinedvar p=new person();//实例化person对象console.log(p.name);//返回 zs//---------//分析:原型链的执行过程 function Person(name,age,address){ this.name=name; this.age=age; this.address=address; } Person.prototype.hello=function(){ return "大家好,我叫:"+this.name+",今年"+this.age+"岁了。家住在:"+this.address; }
function Student(grade,sex){ this.grade=grade; this.sex=sex; } //谨记:指定prototype原型时,一定要为其父对象以及实际参数。 //说白了就是以某某为模板(基础)作为创建对象的标准。 Student.prototype=new Person("zs",18,"清源乡"); Student.prototype.showInfo=function(){ return "我读"+this.grade+"年纪了,我的性别是:"+this.sex; } //Person {name: undefined, age: undefined, address: undefined},没有跟参数,默认undefined // console.log(new Person()); var stu=new Student(3,"male"); //以下是stu.__proto__本身原型链对象的属性 console.log(stu.grade);//3 console.log(stu.sex);//male console.log(stu.showInfo());//我读3年纪了,我的性别是:male //以下是stu父对象person.__peroto__的原型链对象的属性 console.log(stu.name);//zs console.log(stu.age);//18 console.log(stu.hello());//大家好,我叫:zs,今年18岁了。家住在:清源乡 //分析stu.showInfo()原型链的执行过程: //1,执行stu.showInfo()时 // 首先会在stu中查找showInfo方法 // 再到stu.__proto__中查找showInfo方法 //2,因 var stu=new Student(3,"male"); 实例化Student对象 // 所以 stu.__proto__=Student.prototype。 //3,因 Student.prototype=new Person("zs",18,"清源乡"); // 所以 stu.__proto__ = Student.prototype = Person.prototype // 即:会在Person.__proto__中查找showInfo方法 //4,如果还没找到showInfo方法,那么会执行到原型链顶层object.prototype, // 原型链的顶层就是Object.prototype,而这个对象的是没有原型对象的。 //综上所述:原型链就是基于__proto__查找指定属性的过程。 //上诉原型链为: /* stu-->stu.__proto__-->(Student.prototype).__proto__--> (Person.prototype).__proto__-->Object.prototype */</script>
<!-- stu.name取值:在原型链中就近原则取值 --><script type="text/javascript"> function person(){this.say=function(){alert("say");}this.name="1111";} //等价于在person里面声明 this.name="1111";// person.prototype.name="1111";var student=function(){this.name="zheng";this.age=18;/*this.hello=function(){console.log("hello");}*/} // 子方法student.prototype.address="china";student.prototype.hello=function(){console.log("hello");}student.prototype.name="2222";//以下三行代码一起解析:创建student实例的原型为:person,//所有的属性和方法都以person函数里面的定义为标准//按照程序从上到下执行,查找stu.name的值。//1,到stu本身查找。 //this.name="zheng"; 如果没有定义则继续向外查找//2,stu.__proto__ 指向 student.prototype 因student.prototype=new person();//3,所以 stu.__proto__ 直接指向 person.prototype//person.prototype.name="1111";等价于this.name="1111";如果没有定义就报错或者未定义undefined.//4,即:直接在(person.prototype).__proto__ 中查找//5,最后到原型链最顶层Object.prototype为空:null.//上面的student.prototype.name="2222"; 这句话是没有执行的,所以stu.name不可能为2222.//当,如果两行代码的执行先后顺序改变一下,那么stu.name的值就一定会是"2222"。因为给重定义了,如下:先执行 1 再执行 2//1,student.prototype=new person();//2,student.prototype.name="2222";student.prototype=new person();var stu=new student();//实例化student对象console.log(stu.name);//返回: 1111</script>
2,<!-- 构造函数继承 --> 什么是构造函数:就是在函数里面使用 this 关键词来定义属性及其方法在子类内部构造父类对象来实现继承:child.ob=parent;//把父类对象赋值给子类中的一个属性child.ob(name,age,sex);//参数:需要继承父类中的属性,需要继承几个些几个<script>//父类中的参数一定小于等于子类参数,子类参数即属性一定是大于等于父类属性function People(name,age,sex,height){this.name=name;this.age=age;this.sex=sex;this.height=height;this.say=function(){console.log("我的名字叫:"+this.name+",身高为:"+this.height+"年龄:"+this.age+",this.sex:"+this.sex);}}function Student(name,age,height,grade,num){this.obj=People;//在子类中构造一个父类对象,赋值给子类的一个属性//这里只继承People中的name,age,其中sex没有继承,也没有写该值,默认值为undefinedthis.obj(name,age);//需要继承几个属性就写几个属性的参数this.height=height;this.grade=grade;this.num=num;this.hello=function(){//父类中的sex属性没有继承,所以this.sex=undefined//下面的this.name this.age的值是从父类中继承过来的,其他都是子类自定义的。console.log("name:"+this.name+",age:"+this.age+",sex:"+this.sex+",height:"+this.height+",grade:"+this.grade+",num:"+this.num);}}var pe=new People("zs",28,"male",170);console.log(pe.height); //返回 170pe.say();//我的名字叫:zs,身高为:170年龄:28,this.sex:malevar stu=new Student("zheng",30,"male",180,"博士",88);console.log(stu.height);//返回 180console.log(stu.grade);//返回 博士console.log(stu.num);//返回 88stu.say();//返回:我的名字叫:zheng,身高为:male年龄:30,性别:undefined sex未定义stu.hello();//返回 name:zheng,age:30,sex:undefined,height:male,grade:180,num:博士 </script><!-- 使用多种方式进行构造函数继承 --><script>function Animal(name,count){this.name=name;this.count=count;this.getInfo=function(){console.log("动物名称:"+this.name+",数量:"+this.count);}}//第一种方式继承父元素的所有属性及其方法:function Cat(name,count){this.obj=Animal;//普通方式继承this.obj(name,count);}//第二种方式继承父元素的所有属性及其方法:function Dog(name,count){Animal.call(this,name,count);//使用call间接方式继承}//第三种方式继承父元素的所有属性及其方法:function Pig(name,count){Animal.apply(this,[name,count]);//使用apply间接方式继承}var an=new Animal("猴子",88);var cat=new Cat("猫",99);var dog=new Dog("狗",100);var pig=new Pig("猪",200);an.getInfo();cat.getInfo();dog.getInfo();pig.getInfo();</script>
3,call apply 继承
<script>function add(a,b){console.log(a+b);}function substract(a,b){console.log(a-b);}// a.fn.call(b,x1,x2,x3...) == a.fn.apply(b,[x1,x2,x3...])// 表示使用b调用a中的fn的方法,跟的参数是x1,x2,x3.../*add.call(substract,10,3);//返回 13 add.apply(substract,[10,3]);//返回 13 add.call(substract,10,3,4,6,7);//返回 13。多余参数直接忽略,不会报错。*/
function Animal(name){this.name=name;this.getInfo=function(){console.log(this.name);}}function Cat(name){this.name=name;}var ani=new Animal("pig");var cat=new Cat("cat");//使用cat对象调用ani对象的getInfo方法,即说白了就是:/*cat.getInfo=function(){console.log(this.name);}*/ani.getInfo.call(cat);//返回 catani.getInfo.apply(cat);//返回 cat、、、、 //间接调用://1,对象没有call和apply方法,只有函数有//2,apply可以将数组和类数组一次性的传递进函数中,call只能一个一个的传 //解析:call apply 两个都是调用其他对象的方法。 //唯一不同的是参数: //obj.call(otherObj,a,b,c,d...); call的参数是一个一个传 //obj.apply(otherObj,[]); apply的参数是数组 //这里的obj可以是[],{}对象,也可以是全局作用对象window 或者某个局部作用域对象
var name="xm";var person={};person.name="xh";person.getName=function(){return this.name;}console.log(person.getName());//输出:xh//这里的window用于改变person.getName里面的this变成:window,即返回window.name的值console.log(person.getName.call(window));//输出:xm//这里的window用于改变person.getName里面的this变成:window,即返回window.name的值console.log(person.getName.apply(window));//输出:xm//解析:person.getName.apply(window),使用window借用person的getName方法:// window.getName=function(){// return this.name; // }// window.name="xm";
var arr=[2,5];function add(a,b){return a+b;}//直接调用console.log(add(3,6));//使用了间接调用,call(指定作用域对象,argument1,argumen2...)console.log(add.call(window,2,5));//这里间接调用了window下面的arr的数组值, apply(指定作用域对象,数组)console.log(add.apply(window,arr));// 解析:add.apply(window,arr); 使用window借用add的方法,这里add本来前面有一个window。因为add方法本来就是一个全局变量。// window.add(2,5){// return 2+5;// }</script>
两个数组使用concat和apply进行合并<script>var arr1=[1,2,3,4];var arr2=["a","b","c","d"];alert(arr1.concat.apply(arr2,arr1));alert(arr2.concat.apply(arr1,arr2));</script>
javascript 面向对象的声明:Object是所有对象的基类,根,所有的javascript对象都是Object延伸的。
1,字面式对象声明方式var obj={ 属性名称:属性值, 属性名称:属性值, 属性名称:属性值, 属性名称:属性值, 方法名称:function(){}, 方法名称:function(){} } <script> var person={ name:"zhangsan", age:28, address:"清源乡", eat:function(foot){ console.log("我最爱吃:"+foot); }, play:function(game){ console.log("我最喜欢玩:"+game) } } console.log(person.name); person.play("篮球"); person.eat("牛排"); </script>
2,使用new操作符后面跟Object构造函数var obj=new Object();obj.属性名称=属性值;obj.属性名称=属性值;obj.属性名称=属性值;obj.属性名称=属性值;obj.方法=function(str){方法体}<script> var person=new Object(); person.name="zheng"; person.age=28; person.eat=function(a){ console.log("我喜欢吃:"+a); } console.log(person.age); person.eat("苹果");</script>
3,js构造方法声明对象function person([a,b,c]){this.属性名称=a;this.属性名称=b;this.属性名称=c;this.方法名称=function(){方法体...}} <script> //function person(a,b,c,d){ 等价下面一句,参数的[]可以省略 function person([a,b,c,d]){ this.name=a; this.age=b; this.eat=function(c){ console.log("我在吃:"+c); }; this.play=function(d){ console.log("我在玩:"+d); } } //第一次实例化 var zs=new person(["zhangsan","18","牛排","game"]); //var zs=new person("zhangsan","18","牛排","game");上面一句的[]可以省略,等价 console.log(zs.name); zs.eat("love"); //第二次实例化 var xm=new person(["xiaoming","20","土豆片","王者"]); xm.play("王者");//注意:this 代表当前对象,zs 和 xm两个实例是独立的,函数内只能使用this 访问属性和方法。// var num=0; var result=num++; 先赋值再加加 即:result=0,num=0+1=1;// var num=0; var result=++num; 先加加再赋值 即:num=0+1,result=1 </script>
<!-- 封装函数,创建object对象的两种方式: --> <script type="text/javascript"> //工厂模式 function createObject(name,age){ var person = new Object(); person.name=name;//person.name是属性 name是属性值 person.age=age;//person.age是属性 age是属性值 person.say=function(){ alert("大家好,我叫:"+this.name+",今年:"+this.age+"岁!"); }; person.play=function(){ alert("今天天气真好!"); }; return person;//////重点:必须返回person本身////// } //实例化一个对象,可以无限实例化对象 var obj1=createObject("zhangsan",18); console.log(obj1.name);//返回 zhangsan obj1.say();//返回 大家好,我叫:zhangsan,今年18岁! //实例化一个对象,可以无限实例化对象 var obj2=new createObject("lisi",29); console.log(obj2.name);//返回 lisi obj2.play();//返回 今天天气真好! //obj1 和 obj2 没有任何关系,都是不同的对象,不同的个体 //任何模式下,同种模式创建出来的不同对象都是独立存在的,彼此之间是没有关联的。
// 构造函数模式 function per(name,age){ this.name=name; this.age=age; this.action=function(){ console.log("大家好,我叫:"+this.name+",今年:"+this.age+"岁了!"); } } var p1=new per("zs",29); console.log(p1.name); console.log(p1.age); p1.action(); var p2=new per("ls",88); console.log(p2.name); console.log(p2.age); p2.action();
/*构造模式 和 工厂模式 创建对象的区别: 1,构造模式:不会显示创建对象,将属性赋值给this,不需要return对象 2,工厂模式:在方法内部创建object对象,返回object对象,属性和方法都是赋值给object对象*/ </script>
<!-- 工厂模式 中的this是指window,而不是object对象,别弄错了--> <script type="text/javascript"> function fn(name,age){//window.fn...父级作用域是window,所以里面也是一样 var person=new Object(); person.name=name; person.age=age; //this.name=name;//父级作用域是window //this.age=age;//父级作用域是window console.log(this);//输出对象是:window,查看控制台 return person;//千万记住要返回person对象,否则都是undefined } console.log(fn("zs",18)); console.log(fn("ls",33)); </script>
<!-- js原型模式声明对象 --> 1,任何方法或者函数都自带prototype属性,且它是一个对象。 2,原型模式的根本:函数本身声明为空内容,利用prototype定义一些属性及方法。 function text(){ 执行代码一定是空的。}text.prototype.属性 = 属性值;text.prototype.属性 = 属性值;text.prototype.属性 = 属性值;text.prototype.方法名称 = function(){执行代码}下面是两种方式进行原型模式声明对象<script type="text/javascript"> //js原型模式声明对象function text(){/*一定是空的;*/};text.prototype.name = "ls";text.prototype.age = 18;text.prototype.say=function(){alert("大家好,我的名字叫:"+text.prototype.name+",今年"+text.prototype.age+"岁了!");}//获取name的属性text.prototype.getName=function(){console.log(this);//返回:text {}return text.prototype.name;//这里不能使用text.prototype.name。不然报错。}//重新设置name的属性text.prototype.setName=function(name){return text.prototype.name=name;//这里不能使用text.prototype.name。不然报错。}//原型对象也必须实例化后才进行属性读取和设置。//调用:person.属性名称,中间不用加prototypevar person=new text();//实例化原型对象console.log(person.age);//返回18person.say();//返回:大家好,我的名字叫:ls,今年18岁了!console.log(person.getName());//返回 lsconsole.log(person.setName("zheng"));//设置name的属性:zheng</script><!-- 使用json的方式重写了js原型模式声明对象 --><script type="text/javascript"> function text(){};//这里使用json的方式重写了js原型模式声明对象text.prototype={name:"zs",age:23,say:function(){alert("大家好,我的名字叫:"+text.prototype.name+",今年"+text.prototype.age+"岁了!");},getName:function(){return text.prototype.name;//这里不能使用text.prototype.name。不然报错。},setName:function(name){return text.prototype.name=name;//这里不能使用text.prototype.name。不然报错。}}//原型对象也必须实例化后才进行属性读取和设置。//调用:person.属性名称,中间不用加prototypevar person=new text();//实例化原型对象console.log(person.age);//返回18person.say();//返回:大家好,我的名字叫:ls,今年18岁了!console.log(person.getName());//返回 lsconsole.log(person.setName("zheng"));//设置name的属性:zheng</script>
<!-- js中使用混合模式声明对象(构造函数模式,原型模式) -->语法:function text(a,b,c){this.a=a;this.b=b;this.c=c;}text.prototype.say=function(){return "使用this读取属性a"+this.a;}text.prototype.setA=function(a){return "重置a的值:"+this.a=a;}var obj=new text("ls",18,"China");console.log(obj.a);console.log(obj.say());<script type="text/javascript"> function person(name,age,address){this.name=name;this.age=age;this.address=address;}/*person.prototype.hello=function(){console.log("大家好,我叫:"+this.name+",今天我"+this.age+"岁了!");}person.prototype.getAdd=function(){return this.address;}person.prototype.setAdd=function(address){return this.address=address;}*///上面代码直接简化如下person.prototype={like:"fruite",//随便添加属性或方法hello:function(){console.log("大家好,我叫:"+this.name+",今天我"+this.age+"岁了!");},getAdd:function(){return this.address;},setAdd:function(address){return this.address=address;}}var per=new person("ls",24,"China");console.log(per.like);//返回 furiteconsole.log(per.name);//返回 lsconsole.log(per.getAdd());// 返回 Chinaconsole.log(per.setAdd("American"));//返回 Americanper.hello();// 返回 大家好,我叫:ls,今天我24岁了!</script>