(四)Javascript从原型至对象枚举

(四)Javascript从原型至对象枚举

文章目录

      • (四)Javascript从原型至对象枚举
        • 原型
        • 原型链
        • ==call/apply==
          • 总结:
        • 继承模式
        • 命明空间
        • 思考问题
        • 属性的表示方法
        • 对象的枚举(遍历)enumeration

原型
  1. 定义:

    原型是function对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。原型也是对象。

    //Person函数是对象
    //Person.prototype -- 原型(在被构造函数定义时就产生了)
    //Person.prototype = {} 是祖先(它也是一个对象)
    Person.prototype.LastName = "Wei";//原型的一个属性
    Person.prototype.say = function(){
    	console.log("hehe");
    }//原型的一个方法
    
    function Person(){
       //Person中LastName和say,但是继承了祖先的属性和方法
    }
    var person = new Person();
    var person1 = new Person();
    //person 和 person1 都继承了该原型的属性和方法
    console.log(person.LastName);//得到"Wei"
    
    function Person(name,age){
       this.name = name;
        this.age = age;
    }
    var person = new Person("junjie",19);
    //一个正常的对象有自己的属性和方法也有继承而来的属性和方法
    
    //原型的一般用法:转移构造函数中一些固定的属性和方法到原型中,减小耦合
    
  2. 利用原型特点和概念,可以提取共有属性。

  3. 对象如何查看原型–>隐式属性 __proto__

    Car.prototype = {
    	LastName : "WEI",
    	
    }
    function Car(name){
    	this.name = name;
    }
    var car = new Car("junjie");
    console.log(car.__proto__);
    //结果是原型的对象,点开里面有constructer:f Car(name)和__proto__
    //可以通过命明规则来说明自己私人的属性,比如 var _pravate=~;
    
    
    //过程:在用new构造对象的时候,发生下面过程
    //在Car 里面 var this = { __proto__ : Person.prototype};
    //这里的作用相当于连接原型和构造函数
    //每一个新产生的car中都有__proto__属性来放原型
    //在查找属性LastName时,car会先在Car中查找,然后顺着__proto__再在Car.prototype中查找
    //同样可以用于修改, car.__proto__ = obj{LastName : "Wang"}
    
    

    注意

    Person.prototype.name = "sunny";
    function Person(){
    	//var this = {__proto__:Person.prototype}
    }
    var person = new Person();
    Person.prototype = {
    	name : "cherry"
    }
    
    console.log(person.name);
    //注意这里得到的结果是"sunny"而不是"cherry"
    //因为在声明新对象person的时候,它的__proto__连接的是原来的Person.prototype
    /* 过程理解大致如下:
    首先是预编译,提升变量person和function Person
    开始执行。先给Person的原型增加新属性name : "sunny"
    然后person = new Person(),person变成一个对象
    person.__proto__里面的存的是 obj={name:"sunny",constructor:Person()}
    下一步执行Person.prototype={name:"cherry"}
    因为person的proto属性早就被定义好了
    所以这里的修改该的是原型的属性,而不是person中的__proto__,
    person在自身属性找不到name时,会在__proto中查找;
    
    简化流程:
    Person.prototype = {name : "sunny"}
    person.__proto__ = Person.prototype;
    Person.prototype = {name : "cherry"}
    */
    
    //注意顺序会影响结果,如果将6~8行代码,放在peoson = new Person()之前
    //结果就是"cheery"
    
  4. 独立生成对象如何查看对象的构造函数 --> constructor

    function Car(){
    
    }
    var car = new Car();
    console.log(car.constructor);
    //这里的结果会显示构造函数,说明原型中有这个constructor属性,这是系统自动加的
    /*
    原型中Car.prototype = {
    	constructor:Car(),
    	_proto_:Object
    }
    */
    //这时候可以改变constructor指向 
    //一种方式:Car.prototype.constructor = Person;
    //另一种方式:Car.prototype = {constructor : Person}
    console.log(car.constructor);
    //得到的是 Person(){}
    
原型链
  • 如何构成原型链?

    • 概念的引入,在用__proto__查看对象的原型时,我们发现原型中也有属性__proto__
//原型的__proto__属性,Grand.prototype.__proro__等于Object.prototype  
  //Object.prototype是所有对象的最终原型
  //访问Object.prototype.__proto__得到null,说明这是终端
  Grand.prototype.lastName = "Deng";
  function Grand(){
  		this.name = "daming";
  }
  var grand = new Grand();
  Father.prototype = grand;//grand是构造函数Father的原型
  function Father(){
  	this.name = "xiaoming";
  }
  var father = new Father();
  function Son(){
  	this.name = "xiaoxiaoming";
  }
  Son.prototype = father;//father是构造函数Son的原型
  var son = new Son();
  console.log(son.lastName);
  /*这里通过原型链访问,son-->son.__proto__(即father)
  -->father.__proto__(即grand)-->grand.__proto__,lastName
  有点类似作用域链
  • 原型链上属性的增删改查

    • 一般子不能修改父,因为只会在自身上面增加新属性

      //沿用上例
      function Father(){
      	this.name = "xiaoming";
      	fortune = {
      	card1 : "visa",
      	card2 : "master"
      	}
      }
      //访问son.fortune可以看到父元素的fortune对象
      son.fortune = {card1:"mi",card2:"huawei"};
      console.log(father.fortune);//这里father中的fortune对象没改
      //变的是son中增加了对象fortune,里面含两个属性
      
      
      //但是以下方式可修改(忽略第10 11行代码)
      son.fortune.card2 = "mi";
      //因为fortune后面跟属性,可以说forture是引用值
      //这里相当于son访问的是引用值fortune的card2这个原始值
      
      //沿用上例
      function Father(){
      this.num = 100;
      }
      son.num++;
      //结果是son中增加了一个新属性num 值是101,而father中没有变
      
      
      
      
    • this的归属问题

      
      Person.prototype = {
          name : "a",
          sayName : function(){
              console.log(this.name);
          }
      }
      function Person(){
          name : "b";
      }
      var p1 = new Person;
      p1.name = "c";
      console.log(p1.sayName());
      //这里的检索过程应该是
      //p1-->p1.__proto__(即Person.prototype)
      //找到函数体后执行,this.name
      //这时候的this指的是p1,因为是p1过来调用的
      //所以结果是"c",如果没有第37行代码,结果就是"b"
      //因为name = "b" 是所有新造对象都共有的
      
  • 绝大多数对象的最终都会继承自身Object.prototype

var obj1={}//这是一个自变量对象的创建
//其实质是调用了系统函数即 var ovj1 = new Object();
//但一般都不用调用系统函数的写法。

//obj1.__proto__ ----> Object.prototype
//绝大多数对象的最终都会继承自身Object.prototype


//例外(联系下面的Object.create())



  • Object.create(原型);
//var obj = Object.create(原型);
var obj = {name : "sunny" , age : 123};
var obj1 = Object.create(obj);
//obj1是一个对象,它的原型是obj

//利用create将对象的构造函数的原型中的属性提给对象
Person.prototype.name = "sunny";
function Person(){
    
}
var person = Object.create(Person.phototype);

//Object.create()中的括号不能不填,不填会报错
//报错说里面必须是 对象(可以是数组) 或者 null
//如果填入了 null,这是构造出来的对象是没有原型的
var person = Object.create(null);
console.log(person.toString());
//toSpring是Object.prototype中的一个方法,是显示类型转换的系统函数
//这里报错会说没有
person.__proto__ = Object.prototype;
console.log(person.toString());
//这里人为的加入Object.prototype是没有用的.

toString拓展

console.log(ture.toString());
//得到"ture"

console.log(123.toString());
//这种调用是错误的,因为123.中的“.”会当成浮点型
//要掉用需要一个变量存123

方法的的重写


var num = 123;
console.log(num.toString());
//-->因为num是原始值,所以会用到包装类 new Number(num).toString();
/*
	Number.prototype.toString = function(){
	//这里是Object中的方法的 "重写"
	//Number的原型中是自带toString的,这里不是Object的toString
		return "hehe";
	}
	*/
Number.prototype.toString = function(){
    return "h1h1";
}
var k = 123;
console.log(k.toString());
//此处返回了h1h1


call/apply
  • 作用 :

    改变this指向

    function Person(name,age){
    	this.name = name;
    	this.age = age;
    }
    var person = new Person("deng",100);
    var obj = {}
    //一般来说执行一个函数test()相当于teat.call(),call是一个方法
    Person.call(obj,"cheng",300);
    //这里让Person中所有的this都变成obj,同时传入参数
    /*得到
    obj = {name:"cheng",age=300}
    
    //实质:利用call来给obj按Person工厂来制作属性
    

    实现过程(借用别人函数实现自己功能)

    function Person(name,age,sex){
    	this.name = name;
    	this.age = age;
    	this.sex = sex;
    }
    function Student(name,age,sex,tel,grade){
    	this.name = name;
    	this.age = age;
    	this.sex = sex;
    	this.tel = tel;
    	this.grade = grade;
    }
    var student = new Student("wei",19,"male",137,2020);
    
    /* 用法:
    将第7~9行变为 Person.call(student,name,age,sex);
    但是上面这个具有唯一性,只针对student
    所以要改为 Person.call(this,name,age,sex);
    */
    function Student(name,age,sex,tel,grade){
    	Person.call(this,name,age,sex);	
    	this.tel = tel;
    	this.grade = grade;
    }
    

    apply和call区别不大

  • 区别

    后面传的参数形式不同 。

    call 需要把实参按照形参的个数传进去

    apply 只能传一个参数,是数组

    Person.apply(this,[name,age,sex]);
    
总结:

两者功能相同,都是改变this指向,但传参列表不同。

继承模式
  1. 传统继承–>原型链

    过多的继承了没用的属性

      //参考上方原型链中的祖先 父亲 后代
    
  2. 借用构造函数(沿用上例call)

    • 不能继承借用构造函数的原型

      Student的原型仍然是Student.prototype

    • 每次构造函数都要多走一个函数(操作上和运行上一般般)

      因为call每次构造函数都要多走一次Person函数

  3. 原型共享(比较好)

    Father.prototype.lastName = "Deng";
    function Father(){
    
    }
    function Son(){
    
    }
    Son.prototype = Father.prototype;
    var son = new Son();
    var father = new Father();
    
    //封装一个函数实现继承
    //继承的两中语法
    //function extend(){}
    function inherit(Target,Origin){
        Target.prototype=Origin.prototype;
    }
    //表示Target继承至Origin,传进来的应该是构造函数
    /*
    这里要注意顺序的问题:
    要先执行封装函数,再赋值son= new Son();
    否则不生效
    */
    
    • 不能随意改动自己的原型(改变会影响他者的原型)
  4. 圣杯模式

    (方法仍然是公有原型)

    Father.prototype.lastName = "Deng";
    function Father(){
    
    }
    function C(){
        
    }
    function Son(){
    
    }
    C.prototype = Father.prototype; 
    //Father.prototype是Father和C的公有原型
    Son.prototype = new C();
    //此处通过原型链的方式来形成关系,Son可通过原型链爬到Father.prototype
    //当增加Son.prototype.qq增加到的是C中
    
    /*封装函数:
    function inherit(Target,Origin){
    	function F(){};
    	F.prototype = Origin.prototype;
    	Son.prototype = new F();
    	
    	Target.prototype.constructor = Target;
    	//修正信息用
    	Target.prototype.uber = Origin.prototype;
    	//此处用于说明继承的对象是谁
    }
    */
    //高端的写法
    /*
    var inherit = ( function (){
    	var F = function(){};
    	return function(targer,Origin){
    	
    	F.prototype = Origin.prototype;
    	Son.prototype = new F();	
    	Target.prototype.constructor = Target;
    	Target.prototype.uber = Origin.prototype;
    这里是利用了闭包封装私有化的思想,F函数体存于圣杯函数
    的作用域中使用,成为圣杯函数的私有化变量,因为F是隐式的
    附加的东西,所以这是一个很好的方法
    	}
    }
    ());
    */
    
命明空间
  • 管理变量,防止污染全局,适用于模块化开发

    • 一个文件里面声明的变量可能与另一个文件中变量相同,导致覆盖。4

      解决办法:命明空间

    <script type="text/javascript">
    	var org = {
    		departmeng1:{
                jicheng:{
                    name : "abc",
                  age : 123
                },
                xvsong:{
                    
                }
            },
            	departmeng2:{
                zhangsan:{
                    name : "kkk",
                    age : 4
                },
                xvgao:{
                    
                }
            },
    	
    	}
    	var jicheng = org.department1.jicheng;
    
    //通过闭包实现私有化
    //姬成课程13,后面补充
    </script>
    
    
    
思考问题
  • 如何实现链式调用模式 (模仿jquery)

    var deng = {
        smoke : function (){
            console.log("Smoking");
        }
        drink : function (){
            console.log("drinking");
        }
    	perm : function (){
            console.log("preming");
        }
    }
    deng.smoke().drink();
    //这里无法连用,因为smoke返回的是undefined
    //所以只需要在3-4行代码中间加个 return this;这里的this就是deng
    
  • obj.eat().smoke().drink().eat().sleep();

属性的表示方法
var obj  = {
    name:"abc"
}
//每次访问obj.name ----> 隐式转化位obj["name"]
//另一种属性表示方法,注意要放"name"不是变量name
//后者更灵活,因为可以利用字符串拼接


var deng = {
    wife1 : {name : "xiaoliu"},
    wife2 : {name : "xiaozhang"},
    wife3 : {name : "xiaohong"},
    wife4 : {name : "xiaoqi"},
  	sayWife : function (num){
        return this["wife" + num]
;    }	
}

对象的枚举(遍历)enumeration
  • for in
//目的:遍历一个对象,知道里面的属性值
var obj = {
	name : "123",
    age : 123,
    sex : "male",
    height : 180,
    weight : 75
}
//forin循环
for(var prop in obj){
    console.log(prop + " " + typeof(prop));//都是字符类型
}
//说明检索的属性是字符
//prop相当于一个变量,存属性名,每一圈循环都不一样,prop名字可自定义

var obj = {
	name : "123",
    age : 123,
    sex : "male",
    height : 180,
    weight : 75,

}
for(var prop in obj){
    console.log(obj.prop);
}
//这里得到的都是undefined
//obj.prop ----> obj["prop"]
//说明在obj中检索"prop"这个字符串,这时的prop不是一个变量

var obj = {
	name : "123",
    age : 123,
    sex : "male",
    height : 180,
    weight : 75,
	prop : 234
}
for(var prop in obj){
    console.log(obj.prop);
}
//这里得到的六个结果都是234
//更正方法obj.prop改为obj.[prop]

  1. hasOwnProperty(重要)
var obj = {
	name : "123",
    age : 123,
    sex : "male",
    height : 180,
    weight : 75,
    __proto__ : {
        lastName : "deng"
    }
}

for(var prop in obj){
    console.log(obj.[prop])
}
//这时候也会把原型中的lastName显示出来,但我们的目的是让它不显示
//利用obj.hasOwnProperty(prop)这个方法返回布尔值这个方法来实现

for(var prop in obj){
    if(obj.hasOwnProperty(prop)){
    console.log(obj.[prop]);
    }
}
//prop会延展到原型,但遇到终端Object.prototype时不会遍历它
//但是如果手动设置Object.prototype = {abc:"123"}就会显示

var obj = {
	name : "123",
    age : 123,
    sex : "male",
    height : 180,
    weight : 75,
    __proto__ : {
        lastName : "deng"
    }
}

Object.prototype.abc = "123";
for(var prop in obj){
    if(!obj.hasOwnProperty(prop)){
    console.log(obj.[prop]);
}
//这时候得到结果是 "deng" 和 "123"
//小结:prop不会遍历到系统的原型
  1. in(使用概率极低)
//用来看属性是不是属于对象的,注意前面是要写字符串类型的属性名
"height" in obj
//得到true
//但缺点在于"lastName" in obj得到也是obj
//所以in只能判断这个对象上面能不能访问到这个属性
//并不是判断这个属性属不属于这个对象
  1. instanceof(重要,常考)

    function Person(){
        
    }
    var person = new Person();
    //A instanceof B
    //A 是不是 B构造函数构造出来的
    /*
    person instanceof Person 这里得到的是true
    但是 person instanceof Object 也是true
    //数组也是对象,[]是空数组
    [] instanceof Array 是true
    但是[] instanceof Object 也是true
    
    总结:看A对象的原型链上 有没有 B的原型
    
    

    它可以解决的一个比较强大的问题

    var arr = [] || {}
    //注意:对象和数组的typeof都是object
    //现在 如何判断arr是数组还是对象
    
    /*①
    if(arr instanceof Array){
        //这是一个数组
    }
    */
    
    /*②
    数组因为也是对象,所以也有constructor
    console.log([].constructor);
    这里得到 function Array{[native code]}
    而对象的constructor是function Object{[native code]}
    
    */
    
    /*③
    用法:Object.prototype.toString.call([]);
    控制台得到[object Array]
    
    Object.prototype.toString.call(123);
    控制台得到[object Number]
    
    Object.prototype.toString.call({});
    控制台得到[object Object]
    
    原因
    下面是一个方法
    Object.prototype.toString = function(){
    	//里面有this
    	//谁调用this就是谁
    	
    	//识别this,返回相应的结果
    	//这里的this是数组
    }
    */
    
    

你可能感兴趣的:(JS学习笔记,javascript,js)