原型链
- 实例对象与原型之间的连接,叫原型链
\_proto\_
(隐式连接)Object
对象是原型链的最外层
function AAA(){
this.name = "小明";
}
AAA.prototype.showName = function(){
console.log(this.name);
}
var a = new AAA();
a.showName(); //小明
- 根据一些数组的方法,可以推断出js源码中,系统对象也是基于原型的程序。
- 尽量不要去修改或添加系统对象下的方法和属性
var arr = [1,2,3];
Array.prototype.push = function(){}
arr.push(4,5,6);
console.log(arr); //[1,2,3]
var arr = [1,2,3];
Array.prototype.push = function(){
// this:1,2,3
// arguments:4,5,6
for (var i = 0; i < arguments.length; i++) {
this[this.length] = arguments[i];
}
return this.length;
}
arr.push(4,5,6);
console.log(arr); //[1,2,3,4,5,6]
对象的组成
- 方法(操作、行为)—— 函数:过程、动态的
- 属性 —— 变量:状态、静态的
工厂方式 --- 面向对象中的封装函数
- 改成与系统对象类似写法:首字母大写、New关键字提取、 This指向为新创建的对象
- 构造函数
- 存在的问题:对象的引用,浪费内存
- 当new去调用一个函数:这个时候函数中的
this
就是创建出来的对象,而函数的返回值直接就是this
(隐式返回) - new后面调用的函数:叫做构造函数
function CreatePerson(name){
this.name = name;
this.showName = function(){
console.log(this.name);
}
}
var p1 = new CreatePerson('小明');
p1.showName(); //小明
var p2 = new CreatePerson('小强');
p2.showName(); //小强
基本类型,赋值的时候只是值的复制
对象类型,赋值不仅是值的复制,而且也是引用的传递
var arr1 = [1,2,3],
arr2 = [1,2,3];
console.log(arr1==arr2); //false
var a = [1,2,3];
var b = a;
b.push(4);
console.log(a); //[1,2,3,4]
var a = [1,2,3];
var b = a;
b = [1,2,3,4]; //内存中是直接重新给b一个新的内存指向
console.log(a); //[1,2,3]
原型 prototype
- 原型可以用来重写对象方法,让相同方法在内存中存在一份(提高性能)
- 通过原型改写工厂方式:原则:相同的属性和方法可以加在原型上、混合的编程模式
- 总结面向对象写法:构造函数+属性,原型+方法
原型:去改写对象下面公用的属性或方法,让公用的属性或方法在内存中存在一份(提高性能)
var arr = [1,2,3,4,5],
arr2 = [2,2,2,2,2];
Array.prototype.sum = function(){
var result = 0;
for (var i = 0; i < this.length; i++) {
result += this[i];
}
return result;
}
console.log(arr.sum()); //15
console.log(arr2.sum()); //10
- 关于优先级
var arr = [];
arr.number = 10;
Array.prototype.number = 20;
console.log(arr.number); //10
function CreatePerson(name) {
this.name = name;
}
CreatePerson.prototype.showName = function(){
console.log(this.name);
}
var p1 = new CreatePerson('小明'); //小明
var p2 = new CreatePerson('小强'); //小强
console.log(p1.showName()==p2.showName()); //true
console.log(p1.showName()===p2.showName()); //true
关于this指向:
情形1:
oDiv.onclick = function(){
this:oDiv //因为调用方式是oDiv.onclick()
}
情形2:
oDiv.onclick = show;
function show(){
this:oDiv //因为调用方式是oDiv.onclick()
}
情形3:
oDiv.onclick = function(){
show();
}
function show(){
this:window
}
包装对象
- js,基于原型
String
Number
Boolean
var str = new String('hello');
console.log(typeof str); //Object
var str = 'hello';
console.log(str.charAt(0));//基本类型会找到对应的包装类型对象,然后包装对象把所有的属性和方法给基本类型,然后包装对象消失
//原型中是String.prototype.charAt = function(){……}
//用原型方式封装一个返回最后一个字母的函数
var str = 'hello';
String.prototype.lastValue = function(){
return this.charAt(this.length-1);
}
console.log(str.lastValue()); //o
var str = 'hello';
str.number = 10;
console.log(str.number); //undefined
//因为String类型下并没有number属性
var str = 'hello';
String.prototype.number = 20;
str.number = 10;
console.log(str.number); //20
//string是基本类型,给基本类型属性是无效的
//js基本类型:string boolean number
- 原型链的逐层寻找(直接对象--原型--object)
function Aaa(){
}
Aaa.prototype.number = 10;
var a1 = new Aaa();
console.log(a1.number); //10
function Aaa(){
this.number = 20;
}
Aaa.prototype.number = 10;
var a1 = new Aaa();
console.log(a1.number); //20
function Aaa(){
}
Object.prototype.number = 30;
var a = new Aaa();
console.log(a.number); //30
原型链查找示意图:
- 面向对象的一些属性和方法:
hasOwnProperty
看是不是对象自身下的属性
constructor
查找对象的构造函数
每个原型都会自动添加constructor属性
For in时有些属性是找不到的
避免修改constructor属性
instanceof
运算符
对象与构造函数在原型链上是否存在关系
toString()
Object上的方法
function Aaa(){}
var a1 = new Aaa();
console.log(a1.hasOwnProperty);
console.log(a1.hasOwnProperty==Object.prototype.hasOwnProperty); //true
a1.hasOwnProperty
打印结果为:
var arr = [];
arr.num = 10;
Array.prototype.num2 = 20;
console.log(arr.hasOwnProperty('num')); //true
console.log(arr.hasOwnProperty('num2')); //false
function Aaa(){}
var a = new Aaa();
console.log(a.constructor); //ƒ Aaa(){}
// Aaa.prototype.constructor = Aaa; //每个函数都会有,系统自动生成
var arr = [];
console.log(arr.constructor); //ƒ Array() { [native code] }
console.log(arr.constructor==Array); //true
console.log(arr.constructor===Array); //true
function Aaa(){}
Aaa.prototype = {
constructor: Aaa,
age: 20,
name: '小明'
};
var a = new Aaa();
console.log(a.constructor); //ƒ Aaa(){}
function Aaa(){}
Aaa.prototype = {
age: 20,
name: '小明'
};
var a = new Aaa();
console.log(a.constructor); //ƒ Object() { [native code] }
function Aaa(){}
var a = new Aaa();
console.log(a instanceof Aaa); //true
console.log(a instanceof Array); //false
console.log(a instanceof Object); //true
toString
系统对象下面都是自带的,自己写的对象则是通过原型链找object
下面的
var arr = [];
console.log(arr.toString()==Object.prototype.toString()); //false
function Aaa(){}
var a1 = new Aaa();
console.log(a1.toString()==Object.prototype.toString()); //true
var arr = [1,2,3];
console.log(arr.toString()); //1,2,3
Array.prototype.toString = function(){
return this.join('+');
}
console.log(arr.toString()); //1+2+3
- 利用
toString
做类型判断
var arr = [];
console.log(Object.prototype.toString.call(arr)); //[object Array]
console.log(Object.prototype.toString.call(arr)=='[object Array]'); //true
var re = new RegExp();
console.log(Object.prototype.toString.call(re)); //[object RegExp]
var a = null;
console.log(Object.prototype.toString.call(a)); //[object Null]
constructor
instanceof
在跨页面时(比如iframe
),不能正确判断对象类型,所以最好用toString
方式做类型判断
var oF = document.createElement('iframe');
document.body.appendChild(oF);
var ifArray = window.frames[0].Array;
var arr = new ifArray();
console.log(arr.constructor==Array);//false
console.log(arr instanceof Array);//false
console.log(Object.prototype.toString.call(arr)=='[object Array]');//true
对象的继承
继承
- 在原有对象的基础上略作修改,得到一个新的对象
- 不能影响原有对象的功能
- 子类不影响父类,子类可以继承父类的一些功能(代码复用)
如何继承
- 属性:调用父类构造方法 call
- 方法:for in(拷贝继承)(jq也是采用拷贝继承extend)
function CreatePerson(name,sex){ //父类
this.name = name;
this.sex = sex;
}
CreatePerson.prototype.showName = function(){
console.log(this.name);
}
var p1 = new CreatePerson('小明','男');
function CreateStar(name,sex,job){ //子类
CreatePerson.call(this,name,sex);//使用call继承父类属性
this.job = job;
}
CreateStar.prototype = CreatePerson.prototype;//这种继承父类方法的方式会影响到父类
CreateStar.prototype.showJob = function(){}
var p2 = new CreateStar('黄晓明','男','演员');
console.log(p1);
console.log(p2);
CreateStar.prototype = CreatePerson.prototype;
这种方式去继承父类方法会对父类方法产生影响,之前知道for in
可以实现对原对象无影响的拷贝,如:
var a = {
name: 'Alice'
};
var b = {};
for (var attr in a) {
b[attr] = a[attr];
}
b.name = 'Bob';
console.log(a.name);//Alice
console.log(b.name);//Bob
所以在继承父类方法时使用封装的这种for in方法:
function extend(obj1,obj2){
for (var attr in obj1) {
obj2[attr] = obj1[attr];
}
}
function CreatePerson(name,sex){ //父类
this.name = name;
this.sex = sex;
}
CreatePerson.prototype.showName = function(){
console.log(this.name);
}
var p1 = new CreatePerson('小明','男');
function CreateStar(name,sex,job){ //子类
CreatePerson.call(this,name,sex);//使用call继承父类属性
this.job = job;
}
extend(CreatePerson.prototype,CreateStar.prototype);//使用for in继承父类方法
CreateStar.prototype.showJob = function(){}
var p2 = new CreateStar('黄晓明','男','演员');
console.log(p1);
console.log(p2);
继承的其他形式
- 原型继承——借助原型来实现对象继承对象
- 类式继承——利用构造函数(类)继承的方式
function Aaa(){ //父类
this.name = '小明';
this.arr = [1,2,3];
}
Aaa.prototype.showName = function(){
console.log(this.name);
}
function Bbb(){ //子类
}
Bbb.prototype = new Aaa();//----------------------------类式继承实现句1
var b1 = new Bbb();
b1.showName(); //小明
console.log(b1.name); //小明
console.log(b1.constructor); //function Aaa(){ //父类
//this.name = '小明';
//this.arr = [1,2,3];
//}
//所以要修正指向问题
Bbb.prototype.constructor = Bbb;//-----------------------类式继承实现句2
console.log(b1.constructor); //function Bbb(){ //子类
//}
b1.arr.push(4);
console.log(b1.arr);//[1,2,3,4]
var b2 = new Bbb();
console.log(b2.arr);//[1,2,3,4]
//所以要修正影响继承属性的问题
实现类式继承:(属性和方法分开继承)
关于原型继承:
综上,继承的三种方式:拷贝继承、类式继承、原型继承
实例:拖拽
/**
#div1 {
position: absolute;
width: 100px;
height: 100px;
background-color: pink;
}
<div id=“div1"></div>
**/
window.onload = function(){
var oDiv = document.getElementById("div1");
var disX = 0,
disY = 0;
oDiv.onmousedown = function(ev){
var ev = ev||window.event;
disX = ev.clientX-oDiv.offsetLeft;
disY = ev.clientY - oDiv.offsetTop;
document.onmousemove = function(ev){
var ev = ev||window.event;
oDiv.style.left = ev.clientX - disX + 'px';
oDiv.style.top = ev.clientY - disY + 'px';
console.log(oDiv);
}
document.onmouseup = function(){
document.onmousemove = null;
document.onmouseup = null;
}
return false; //阻止默认行为
}
}
- 改为面向对象的方式写法:
function Drag(id){
this.oDiv = document.getElementById(id);
this.disX = 0;
this.disY = 0;
}
Drag.prototype.init = function(){
var This = this;
this.oDiv.onmousedown = function(ev){ //ev是事件对象里的,必须放在事件函数中
var ev = ev||window.ev;
This.fnMove(ev);
return false; //阻止默认行为也是必须放在事件函数中
}
}
Drag.prototype.fnMove = function(ev){
var This = this;
This.disX = ev.clientX - This.oDiv.offsetLeft;
This.disY = ev.clientY - This.oDiv.offsetTop;
This.oDiv.onmousemove = function(ev){
var ev = ev||window.ev;
this.style.left = ev.clientX - This.disX + 'px';
this.style.top = ev.clientY - This.disY + 'px';
this.onmouseup = function(){
this.onmousemove = null;
this.onmouseup = null;
}
}
}
window.onload = function(){
var oDiv = new Drag("div1");
oDiv.init();
}