包装对象
为什么字符串,数字等基础类型是没有属性和方法的。那么我们平时创建了一个字符串(非字符串对象)的时候,为什么可以调用他length,indexOf等属性或方法?
内置对象:js提供已经定义好的对象,如Array,Date,RegExp,String,Number,Boolean等,同时js定义好了一些对应的一些构造函数
为什么字符串,数字等基础类型是没有属性和方法的。那么我们平时创建了一个字符串(非字符串对象)的时候,为什么可以调用他length,indexOf等属性或方法?
这就是包装对象:
当我们去调用字符串,数字,布尔值这三种基础数据类型的属性和方法的时候,他们本身是不具备属性和方法的,但是js内部,会自动的去调用他们对象的构造函数,然后把他们的值作为参数进行传入,然后执行对应属性或方法,并把结果进行返回。
包装对象一共有三个: String,Number,Boolean
var a = String('123')
console.log(typeof a,a) //String 123
var b = new String('123')
console.log(typeof b,b) //Object {0:'1',1:'2',2:'3'}
/*可以看到是一个对象,在它的__proto__里的有各种方法*/
//创建一个基本字符串类型的数据
var str = "abcdefg";
//调用方法 indexOf ,str本身是没有indexOf方法的
var s = str.indexOf("b");
//那么js会自动在内部调用str的对应构造函数方法,同时,把str的值作为该次的调用的参数
new String(str).indexOf("b")
var s2 = new String(str).indexOf("b");
String就是一个字符串数据的包装对象
如果我们尝试给字符串,数字,布尔值增加自定义的属性和方法的时候,是无效的
var str2 = "abcdef";
str2.a = 100;
str2.length = 1000;
console.log(str2.a); // undefined 相当于new String(str2).a
console.log(str2.length); // 6 相当于new String(str2).length
所以当一个字符串被申明以后,其值就不会发生改变,除非重新覆盖,所以我们把字符串值又成为字符串常量 - 字面量
修改toString()
Object对象prototype下的一个方法,所有对象都继承该方法
既然是在Object的prototype里面,那就可以自己修改toString方法
function Creat(){}
var c1 = new Creat;
Object.prototype.x = 100;
console.log(c1.x) //因为之前说过,自身找不该属性方法,就会一直往上找,最后找到Object
Creat.prototype.toString = function(){
console.log("我是构造函数toString")
}
c1.toString(); //"我是构造函数toString";
Array.prototype.toString = function(){
var result = 0;
for (var i = 0;i < this.length;i++){
result += this[i]
}
return result;
}
alert([1,2,3]);//6
hasOwnProperty()
判断某个属性(方法)是否是某个对象自有的,就是非原型链上的
function Person(){
this.name = "小明"
}
Person.prototype.x = 100;
var p1 = new Person();
console.log( p1.hasOwnProperty('name') ); //true
console.log( p1.hasOwnProperty('x') ); //false
constructor
属性:返回某个对象的构造函数(可写)
var a = [1,2,3];
console.log(a.constructor == Array); //true;
a.constructor = 1;
console.log(a.constructor); //1
console.log(a.constructor == Array); //false;
instanceof
属性:判断一个对象是否是某个构造函数的实例化对象
var a = [1,2,3];
console.log(arr instanceof Array); //true
function Creat(){}
var b = new Creat;
cosole.log(b instanceof Creat); //true
当我们使用面向对象的方法,去构建不同的函数时,可能有些元素对象功能是一样的,但有些地方不一样,就需要有不同的方法
继承
拷贝继承
<style type="text/css">
.show {
width: 200px;
height: 200px;
background: red;
}
</style>
<div id="box1"></div>
<div id="box1"></div>
function Creat(elment){
var _this = this;
this.el = elment;
this.el.className = "show";
this.el.onclick = function(){
_this.active();
}
this.el.onmouseover = function(){
_this.hover();
}
this.el.onmouseout = function(){
_this.end();
}
}
Creat.prototype.active = function(){
this.el.style.backgroundColor = "blue"
}
Creat.prototype.hover = function(){
this.el.style.backgroundColor = "green"
}
Creat.prototype.end = function(){
this.el.style.backgroundColor = "red"
}
var box1 = document.querySelector("#box1");
var box2 = document.querySelector("#box2");
new Creat(box1);
//new creat(box2);
如果我们用同一个方法这时就2个元素的属性方法都是一样,点击都会变成蓝色,可我们想每个元素点击使用不同的背景色,这时可以用call改变this指向来完成
function CreatLimit(elment){
Creat.call(this,elment) //Creat,并把Creat中this指向CreatLimit的this
}
//这时还需要把Creat.prototype里面的active给传过来
CreatLimit.prototype = Creat.prototype
//new CreatLimit(box2) 这时还是不行我们点击还是蓝色
CreatLimit.prototype.active = function(){
this.el.style.backgroundColor = "yellow"
}
当我们有多个函数方法相同,但只有一个不一样时需要修改的情况,这时会发现box2点击是黄色的了,但是再点击box1会发现box1也变成黄色了,这就涉及到一个赋值和赋址的情况
赋值和赋址
var a = 1;
var b = a;
b = 2;
console.log(a) //a还是1
var arr1 = [1,2,3];
var arr2 = arr1;
arr2.push(4)
console.log(arr1) //[1,2,3,4]
当我们把arr2和arr1建立关系时,这时他们在内存里(数据都是存储在内存里运行)的地址是同一个,这个时候修改arr2就是修改这个地址的数据,arr1使用这个地址数据就是修改后的,如果是
arr2 = [1,2,3,4] 这个是单独是给arr2赋值,并没有修改地址里的那个数据,所以arr1还是[1,2,3]
如果我们是通过赋址的形式,那么会有问题,因为现在是对象赋址,那么CreatLimit.prototype的修改会影响Creat.prototype
那么我们可以把Creat.prototype中的属性和方法一个个的赋值给CreatLimit.prototype
CreatLimit.prototype.active =Creat.prototype.active
CreatLimit.prototype.hover =Creat.prototype.hover
CreatLimit.prototype.end =Creat.prototype.end
CreatLimit.prototype.active = function(){
this.el.style.backgroundColor = "yellow"
}
//这时因为是单独把值赋给CreatLimit.prototype.active下,在修改覆盖之前的active后,并不会对box1产生影响。
//为了方便。我们可以使用forin在批量动态的处理这些属性和方法,forin不只是会把Drag.prototype自身的属性循环出来,还会把一些原型链上的属性和方法也循环出来
for(var property in Creat.prototype){
CreatLimit.prototype[property] =Creat.prototype[property]
}
CreatLimit.prototype.active = function(){
this.el.style.backgroundColor = "yellow"
}
类式继承
//在上面方法forin更改为
CreatLimit.prototype = new Creat(box1)
CreatLimit.prototype.active = funciton(){
console.log(2)
}
//测试用
var c2 = new CreatLimit(box2)
c2.active();
让CreatLimit的prototype指向Creat的一个实例对象,这样的话CreatLimit的prototype和Creat.prototype就没有直接引用关系,但是因为CreatLimit的prototype是Creat的一个实例,那么CreatLimit的prototype自动会查找Creat的prototype
通过CreatLimit的prototype可以找到Creat的prototype下,但是CreatLimit的prototype和Creat的prototype又没有直接的关系
基于原型链查找过程:
c2.active => c2.proto.active => CreatLimit.prototype.active =>new Creat(box1).proto.active =>
Creat.prototype.active
这只是继承的2种方法,还有其他很多方法,网上都有分享
深度克隆
var obj1 = {
x: 10,
y: 20
}
var obj2 = obj1;
obj2.x = 20;
console.log(obj1.x) //20
console.log(obj2.x) //20
由于:obj2和obj1是引用关系,obj2的修改会影响到obj1,就需要把obj1中的值单独赋值给obj2
var obj2 = {};
for (var propety in obj1){
obj2[propety] = obj1[propety]
}
obj2.x = 100;
console.log(obj1.x) //10
console.log(obj2.x) //100
但是可能obj1里存在对象,或者数组,就还需要在forin到obj1下面的数组里面,可是又是不知道 数组或者对象下面还存不存在数组或者对象,这时就需要递归来寻找
var obj1 = {
x: 10,
y:20,
attr: {a: 1},
arr: [1,2,3],
z: null
};
function extend(originObject) {
var newObject = Array.isArray(originObject)?[]:{};//判断原始数据是数组还是对象
for (var propety in originObject) {
if(typeof originObject[propety] == 'object'&& originObject[propety] != null)//判断是否是个对象
newObject[property] = extend(originObject[property]);
//递归克隆
} else {
newObject[property] = originObject[property];
}
}
return newObject;
}
var obj2 = extend(obj1);
obj2.attr.b = 2;
console.log(obj2.attr);
console.log(obj1.attr);
根据originObject的原始类型来对新对象进行对应的初始化,保证进来什么格式出去就是什么格式,如果出去也是对象,但是传进来是个数组就不行
如果当前数据是对象的话,那么就需要进行深度克隆,注意:typeof来判断数据类型是有一个小的问题的,null的typeof结果也是object,所以需要排除null值的深度克隆
以上!