JS对象
三种创建对象的方法
一、使用关键字new构造函数创建对象
obj = new object()
二、自定义创建对象
//工厂模式创建对象 function createObject(name,age){ var obj = new Object(); obj.name = name; obj.age = age; obj.sayHi = function (){ console.log(this.name,this.age) }; return obj; } var p1=createObject("ss",12); p1.sayHi();
// 自定义构造函数 function Person(name,age){ this.name = name; this.age = age; this.sayHi = function(){ console.log(this.name,this.age); }; } var p1 = new Person("ss",22); p1.sayHi();
this:
永远指向函数正在执行时所在的对象,而不是指向函数创建时所在的对象,(类似Python类中的self,代指实例)
匿名函数或不处于任何对象中的函数都是指向window
函数的this值取决于函数的调用模式:
方法调用模式:当函数作为对象的一个属性时,函数就是这个对象的方法,this指向的就是这个对象
函数调用模式:当函数不作为一个属性,直接调用时,this指向的是全局对象
某个方法中的内部函数的this值也是指向全局对象,而不是外部函数的this
构造函数模式:当使用new方法构造一个函数时,this指向的就是这个新产生的函数对象(实例对象)
call和apply调用:使用这种方法,this指向的是这个方法的第一个参数
var that =this (保存原先this,使得this发生改变时,that依旧指向原先的this)
三、字面量创建方式
obj = {name:“ss”}
类型传递
简单类型(值类型):number ,string ,boolean
复杂类型(引用类型):object
空类型:null , undefined
对象在堆上存储,地址在栈上存储
值类型在栈上存储
值类型传递的是值
引用类型传递的是地址
例:
// 值类型传递 var num1 = 10; var num2 = num1;//传递的是值 function f1(x){ x = 20; } var num = 10; f1(num); console.log(num); //结果是10 ,传递的是值
//引用类型传递 obj = { name : "ss" }; function f1(obj2){ obj2.name = "yy" } console.log(obj.name); //结果是ss f1(obj); console.log(obj.name); //结果是yy ,传递的是地址引用
原型
利用构造函数的原型对象可以实现共享数据,节省内存
构造函数包含原型对象(prototype),prototype中存在constructor构造器,指向自己原型所在的构造的函数
实例对象存在(__proto__),指向该构造函数的原型对象
构造函数的prototype可以被实例对象自接访问
//prototype function Perpon(name,age,sex){ this.name = name; /*这些通过this添加的属性是实例属性*/ this.age = age; this.sex = sex; } Perpon.prototype.sayHi = function(){ /*向构造函数的原型对象中添加属性*/ console.log("My name"+this.name+"\n"+"age"+this.age) }; var p1 = new Perpon("ss",23,"man"); p1.sayHi(); var p2 = new Perpon("yy",25,"man"); /*两个实例对象可以调用相同的原型对象属性*/ p2.sayHi(); console.dir(Perpon); console.dir(p1); /*实例中不存在prototype*/
注意
构造函数中通过this添加的属性都是实例对象属性而不是构造函数的属性
而通过prototype添加的原型对象属性是构造函数的属性,实例属性不存在prototype,但有的支持非标准的__proto__指向原型对象
为原型对象添加属性(两种方式)
Perpon.prototype.eat = function(){}; Perpon.prototype.height = "178cm"; Perpon.prototype.weight = "55kg"; Perpon.prototype = { constructor:Perpon, /*注意这种方式添加原型对象属性方法,需要手动添加构造器*/ height:"178cm", weight:"55kg", eat:function(){} }
原型链:是一种关系,是实例对象和原型对象之间的关系,通过(__proto__)产生关联
原型的指向可以改变
当原型指向发生改变后,再添加方法就可以再使用这个方法
改变原型的指向,可以实现继承
但存在实例后属性值相同的情况,使用借用构造函数方法继承可避免,但借用构造函数不能继承方法
使用组合继承:原型继承+借用构造函数继承
function Perpon(name,age){ this.name = name; this.age = age; } Perpon.prototype.eat = function () { console.log("Perpon eat"); }; function Student(name,sex) { this.name = name; this.sex = sex; } Student.prototype.study = function(){ console.log("going study"); }; Student.prototype = new Perpon("ss",22); /*原型对象指向发生改变,让它指向另外一个实例*/ var st = new Student("yy",23); st.eat();
继承
改变原型的指向,可以实现继承
但存在实例后属性值相同的情况,使用借用构造函数方法继承可避免,但借用构造函数不能继承方法
使用组合继承:原型继承+借用构造函数继承
function Perpon(name,age){ this.name = name; this.age = age; } Perpon.prototype.eat = function () { console.log("Perpon eat"); }; function Student(name,age,sroce) { Perpon.call(this,name,age); /*借用构造函数继承,使得值由子构造函数传入*/ this.sroce = sroce; } //借用构造函数只能解决属性值重复的情况,不能继承方法,需要改变原型指向继承方法 Student.prototype = new Perpon(); /*这里构造函数不传值,因为子构造函数已借用父构造函数*/ var st1 = new Student("ss",22,"100"); console.log(st1.name,st1.age,st1.sroce); st1.eat(); var st2 = new Student("yy",25,"120"); console.log(st2.name,st2.age,st2.sroce); st2.eat();
实例对象中有__proto__原型指向构造函数的prototype,prototype也是对象,它也有__proto__原型,指向object的prototype
而object.prototype.__proto__指向的是null
面向对象对象编程
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> #dv{ width: 200px; height: 300px; background-color: chocolate; } #pp{ width: 200px; height: 300px; background-color: chocolate; } </style> </head> <body> <input type="button" id="btn" value="change"> <div id="dv" ></div> <p id="pp"></p> <script>
function my$(id){
return document.getElementById(id);
}
function ChangeStyle(btnobj,dvobj,json) { this.btnobj = btnobj; this.dvobj = dvobj; this.json = json; } ChangeStyle.prototype.init = function() { var that = this; /*保存这个this,当this改变时,that依然是原先的this*/ this.btnobj.onclick = function() { /*这里this和上面的this不同*/ for (var key in that.json) { /*that这里就是指this,是this.btnobj*/ that.dvobj.style[key] = that.json[key]; } } }; var json = {"width":"300px","height":"500px","backgroundColor":"blue","opacity":"0.2"}; var p1 = new ChangeStyle(my$("btn"),my$("dv"),json); p1.init(); var json1 = {"width":"300px","height":"500px","backgroundColor":"blue","opacity":"0.2"}; var p2 = new ChangeStyle(my$("pp"),my$("dv"),json1); p2.init(); </script> </body> </html>
使得局部变量能在全局使用
<script> //通过自调用访问局部属性 (function (window) { /*接收传过来的window实参*/ function Random(){ } Random.prototype.getRandom = function(min,max){ return Math.floor(Math.random()*(max-min)+min) }; window.Random = Random; /*将内部的变量赋值到顶级对象window下,外部就可用,对象之间传递地址引用*/ })(window); /*将window传入到函数中*/ var rm = new Random(); /*new window.Random(),window可省略*/ console.log(rm.getRandom(0,5)); </script>
在匿名函数直接执行时,将顶级对象window传入到函数中,再将函数内部的属性变量传递给window