对象
- 引用值
- var obj = {
Name : "Tracy",
Age : "18",
Key : "Value",
health : 100,
smoke: function(){this.health --;}
}
注意,对象里的 smoke 方法,想要改变 health 值,也不能直接写 health, 而是要写 this.health 或者是 obj.health; - 对象的方法 obj.smoke();
对象的属性有两种调用方法: obj.Name; obj['Name']; -
属性的增删改查
- 对象增加 wife 属性: obj.wife = "小刘"; //原来有 wife 属性就是增加,没有就是赋值
- 对象属性的删除 delete obj.Name
但是,我们说 window.name 的两种情况
- 一旦经历了 var 操作,所得出的属性,window, 这种属性叫作不可配置的属性
不可配置的属性 delete 不掉
var name = "abc"; window.name 只能查看和修改,删除不掉 (但是删除不会报错) - 一个属性是 window.name = "abc"; 添加出来的,就可以删除 delete window.num
- 一旦经历了 var 操作,所得出的属性,window, 这种属性叫作不可配置的属性
- 对象的属性修改 obj.Name = "Others";
- 对象的属性查看 obj.Name //Tips: 如果打印对象没有的一个属性 比如 obj.abc 那么程序不会报错,而是输出 undefined
- 对象的两种创建方法
- var obj = {} plainObject 对象字面量/对象直接量, 它也有原型,它的原型是 Object.prototype
- 构造函数
- 系统自带构造函数 Object(), Array(), Number()
eg : var obj = new Object(); 完全等于 var obj = {} 没有任何区别 (所以硕对象 obj 的原型是 Object.prototype)
然后自己加属性 obj.name = 'Tracy';
公司里用的都是对象字面量来写,没有任何理由来用 “var obj = new Object();” 来创建对象,又麻烦 又没有用 - 自定义构造函数 (构造函数和正常函数没有任何区别)
function abc(){} //构造函数
var var abc1 = new abc(); //有 new 的话 就是生成了一个对象
abc1.name = 'Tracy'; // 加属性
为了不要给后面的人增加困难,所以我们构造函数的命名规则一定是大驼峰(首字母也大写)
function Car(color){
this.color = color;
this.name = 'BMW';
this.height = '1400';
this.health = 100;
this.run = function (){this.health-- }
}
var car1 = new Car('green');
- 系统自带构造函数 Object(), Array(), Number()
- 构造函数内部原理 这个原理的一切前提都是 这个对象是用 new 来创建的
- 在执行体最前面上加一个隐式的 this 对象: var this{ __proto__: 函数名.prototype}
- 执行 this.XXX = ZZZ;
- 在执行体的最后一行 隐式的返回对象 this
Tips: 由于这里是隐式放回对象,我们就可以捣乱了
eg: function Person(name)
{
// var this{ __proto__: Person.prototype}
this.name = name;
this.age = 10;
return {};
//return this;
}
var a = Person("Tracy"); 此时输出 a 永远都等于 {} 无论我们赋值什么
但是如果我们 return 123; 那么是不影响最后 return this 的,系统会自动忽略 return 123. 因为 new 一个对象的时候 返回值一定得是一个对象,不能是一个原始值,返回其他无效
- 除了 new 了一个对象里面 的 this 代表一个空对象之外,其他函数里面的new, 统统代表 windows 对象
包装类
- var a = "abc"; var b = new String ("abc");
都是字符串,但是 b 是字符串对象了
a 是原始值, b 是引用值
b 是可以加属性的 eg: b.Name = "Tracy";
同理 new Number(); new Boolean();
var num = new Number(123);
var bol = new Boolean(true); - 原始值坚决不能有属性和方法,属性和方法是对象才可以有的
-
包装类就是原始值调用属性的时候,隐式的中间系统帮忙转换的过程
var num = 4;// 原始值 num 为 4
num.leg = 3;//原始值是没有属性的,这里系统发现傻了吧唧的你给加属性了,系统没办法 就给隐式新 new 了一个对象 new Number(4).leg = 3; 新建对象后,系统马上将该对象删除
consol.log (num.leg); // undefined
这里系统发现你竟然又给原始值调用属性了,就有 new Number(4).leg 这里又新建了一个对象,和上一个对象已经没有什么关系了,所以这个对象的leg 属性是 undefined; - var arr = [1,2,3,4,5]; arr.length = 2; 此时 arr = [1,2]; 字符串是被截断了
var str = "12345";
str.length = 2;
此时 str 仍然保持原来的值
var str = "12345";
str.length = 2; //系统发现,你是一个原始值,你没有 length 属性啊,就新建对象 new String("12345").length = 2 这个新建的对象确实被截断了, 然后马上删除该对象 //然后你打印,就发现,被改变的对象马上就删除了,和你原始值一毛钱关系都没有啊. console.log(str.length); //仍然是 4 - undefined 和 null 是没有包装类的,它就是普通的原始值,没有任何方法和属性
比如说 undefined.toString() 是会报错的
原型
- 对我们人类来说, 人类的祖先就是原型
- 原型是 function 对象的一个属性, 它定义了构造函数制造出来的对象的公共祖先。 通过该构造函数产生的对象,可以继承该原型的属性和方法。 原型也是对象
//这里 Person.prototype = {} 就是原型, 它是一个空对象
function Person(){} //构造函数特点,命名大驼峰
var person1 = new Person();
Person.prototype.name = "Tracy"; //祖先添加了属性,那它的所有孙子辈的就自动继承该属性
此时 person1.name 也有值了, 因为它是祖先的孙子- 原型的增删改查
- Person.prototype.newitem = "增,改,查"
- delete Person.prototype.newitem 删
- 原型的应用: 利用原型特点和概念,可以提取共有属性
function Car(color){
this.color = color;
this.carName = "BWM";
this.height = 100;
this.leng = 420;
}
此时每次 new 一个新对象的时候,就会 新建一些一模一样的 height, leng 等固定的值,这就产生了冗余。 此时我们就想到要用 原型。
Car.prototype.height = 100;
Car.prototype.leng = 420;
Car.prototype.carName = "BWM";
function Car(color){
this.color = color;
}
上面的三句原型可以简写为
Car.prototype = {
height : 100,
leng : 420,
carName : "BWM"
} - constructor 对象如何查看对象的构造函数
function Car(){}
var car1 = new Car();
car1.constructor; //constructor 是构造器的意思,返回的是构造这个对象的构造函数
它其实是 Car 的原型里自带的属性,是继承来的属性. 返回的值是 "function Car(){}"
constructor 是系统自动生成的,但是我们其实可以是手动改 constructor 的 比如:
function Person(){}
Car.prototype = {
constructor : Person //它原来系统给默认写的是 Car
}
function Car(){}
var car1 = new Car();
此时 car1.constructor 值为 "function Person(){}",这招就叫作“认贼作父” - 隐式属性 __proto__ 对象如何查看原型
系统自定义属性, 它里面放的就是原型,它把原型和类之间联系到了一起。 在对象自身找不到某属性的时候,它就通过这个 __proto__ 联系,来找原型中的属性
eg: function Person(name)
{// 新建对象的时候 隐式生成 var this{ __proto__: Person.prototype}
this.name = name;
this.age = 10;
return {};
//return this;
}
var a = Person("Tracy"); 此时输出 a 永远都等于 {} 无论我们赋值什么
Person.prototype.name = "abc";
function Person(){}
function obj (){name:"tracy"}
var person1 = new Person();
此时 person1.name 值为 "abc"
person1.__proto__ = obj; //把对象 person1 的原型手动给改了
此时 person1.name 值为 "tracy"
这招就叫作“认贼作祖父” - 一个陷阱
Person.prototype.name = "abc";
function Person(){}
var person1 = new Person();
Person.prototype.name = "funny"
console.log(person1.name); //输出 funny
Person.prototype = {
name: "Tracy"
};
console.log(person1.name); //仍然输出 funny,想想这是为什么呢?
举个例子
var obj = {name:"a"};
var obj1 = obj;
obj = {name:"b"};
此时 obj1.name 为 a; obj.name 为 b, 因为 obj = {name:"b"}; 的时候 obj 完全指向了不同的房间
然后我们分析 Person.prototype.name = "abc";
function Person(){
//var this {__proto__: Person.prototype} //在 new 一个对象的时候 这里会有个隐式声明 this
}
var person1 = new Person();
Person.prototype = {name: "Tracy"}; //注意这里是把它在 new 对象的下面
console.log(person1.name); //输出 abc
注意 "__proto__" 和 "Person.prototype" 就相当于两个对象的赋值, 而后来的 "Person.prototype = {name: "Tracy"};" 已经是说明了 其中一个对象 "Person.prototype" 指向了不同的房间,但是我们的 "__proto__" 仍然指向以前的房间,所以输出的值仍然是原来的 abc
另一个骗局 Person.prototype.name = "abc";
function Person(){}
Person.prototype = {name: "Tracy"}; //注意这里是把它在 new 对象的上面
var person1 = new Person();
console.log(person1.name); //输出 Tracy
这回我们在还没有 new 的时候就改了,所以就直接指向新的了
原型链
- 原型本身自己也是有原型的,那么就形成了一个原型链
- Grand.prototype.lastName = "Deng";
function Grand(){}
var grand1 = new Grand();
Father.prototype = grand1;
function Father(){}
var father1 = new Father();
Son.prototype = father1;
function Son(){}
var son1 = new Son();
这个 son1 可以有它上面的链儿上的所有属性, 原型链的链接点就是 prototype -
Object.prototype 是绝大多数对象的原型链的终端
也就是说 : 绝大多数的对象最终都会继承自 Object.prototype所以 Object.prototype.__proto__ 的值为 null
例外就是: Object.create(null) 创建的对象, 这样创建出来的对象的原型就是 null
-
Object.create(原型)
- 它是一种更加灵活的创建对象的方法,你可以自己指定原型
- var obj = {name: "Tracy"}
var obj1 = Object.create(obj);
这样 obj 就是 obj1 的原型了 - var obj = Object.create(null) ;创建的对象没有原型
那么打印 document.write(obj) 的时候系统就会报错
因为这个 document.write 方法是把里面的东西给加一个 toString() 方法,
就像这样 document.write(obj.toString())
而 obj 没有原型 也就不继承 Object.Prototype.toString 方法,因此报错 - Object.create(原型, 特性)
后面的这个特性,可以设置成可枚举性,只读,读写之类的,以后讲
- 原型链方法的重写:
如果原型里也有一个方法,我们的子类里也写了相同的方法,那么这叫做 “方法的重写” 既: 我覆盖了你原来的 方法。
Eg: Person.prototype = {
toString : function(){return "重写了Object.prototye 里的 toString() 方法";}
}
function Person(){}
var person1 = new Person()
person1.toString();
Object.prototype.toString = function(){return "哈哈";}
var a = {};
a.toString(); //此时返回 哈哈, 因为我们把最底层的 toString 方法给覆盖了
var a = "字符串"
a.toString();//此时返回正常结果,因为 a.toString == new String("aa").toString, 而 String 类里是有自己的 toString 方法的,还没有到 Object.prototype 里取方法那一步
我们也可以很任性把全局的都给改了- Object.prototype.toString = function(){return "哈哈";}
- String.prototype.toString = function(){return "哈哈";}
- Number.prototype.toString = function(){return "哈哈";}
- Boolean.prototype.toString = function(){return "哈哈";}
- Array.prototype.toString = function(){return "哈哈";}
-
This
- aa.action(); // action 方法里面的this : 谁调用的这个方法,this 就指向谁
因此 Demo:
Person.prototype = {
name : "a",
sayName : function(){console.log(this.name);}
}
function Person(){
this.name = "b";
}
var person1 = new Person();
person1.sayName(); //值为 b, this 值向的是 person1
- aa.action(); // action 方法里面的this : 谁调用的这个方法,this 就指向谁