一、初识对象
现实世界的对象:具体的事物,由特征(名词:描述这个对象的)和行为(动词:具体做的事) 组成
程序中的对象:是对现实世界中事物的抽象, 由属性和方法组成 -- 成员
scss
函数和对象的区别:
函数:可以封装一段代码
对象:封装一组属性和方法
函数与方法的区别:
函数:独立存在,调用时通过函数名调用 fn();
方法 :属于对象的函数,称之为 对象的方法 hero.attack( );
区分 静态方法和实例方法:
直接添加到类上的是静态方法: 静态方法通过类名调用
直接添加到类的原型上的是实例方法: 实例方法通过实例调用
function AClass(){} // 定义一个类
AClass.staticMethod = function(){alert("staticMethod")} // 给这个类添加静态方法
AClass.staticMethod() // 静态方法通过类名调用
AClass.prototype.instanceMethod = function(){alert("instanceMethod")} // 给这个类添加实例方法
var a = new AClass(); // 创建一个实例(创建一个对象)
a.instanceMethod(); // 实例方法通过类的实例调用
二、创建对象的方式
1.对象字面量{}
js
var student = { name: '张飞', age: 18, gender: '男',
sayHi: function () { alert('hello'); }
}
// 多个不同的属性 使用,逗号分割
2.new Object()构造函数
js
new 的方式调用构造函数
var hero = new Object(); // 创建了一个空的对象, new Object()等同于对象字面量{}
hero.name = '黄忠'; // 动态的增加属性
hero.weakon = '弓箭';
hero.equipment = ['头盔', '盔甲', '靴子'];
hero.attack = function () {
console.log(this.name + ':射箭');
}
3.工厂方法 批量创造对象
js
function createHero(name, age, gender, blood) {
var hero = new Object(); // 定义一个空对象
hero.name = name;
hero.age = age;
hero.gender = gender;
hero.blood = blood;
hero.attack = function () {
console.log(this.name + ':攻击');
}
hero.run = function () {
console.log(this.name + ':加速跑');
}
return hero; // 返回对象参数 和自定义构造函数比,多了2行代码
}
var hero01 = createHero('黄忠', 18, '男', 100);
var hero02 = createHero('刘备', 18, '男', 100);
console.log(hero01);
console.log(hero02);
4.自定义构造函数
帕斯卡命名,第一个单词的第一个字母大写
调用: new 关键字 调用 构造函数, 通过 一个变量接收 返回值
js
function Hero(name, age, gender, blood) {
this.name = name;
this.age = age;
this.gender = gender;
this.blood = blood;
this.attack = function () {
console.log(this.name + ':攻击');
}
this.run = function () {
console.log(this.name + ':加速跑');
}
}
var hero01 = new Hero('黄忠', 18, '男', 100);
var hero02 = new Hero('刘备', 18, '男', 100);
console.log(hero01);
console.log(hero02);
// this 动态的给对象增加成员,this 指向了当前对象
在自定义构造函数中,new 关键字的执行过程
scss
var p1=new Person();
1、在内存中创建了一个空的对象 (我们把这个对象称之为Person构造函数的实例)-p1
2、让构造函数中的this 指向刚刚创建的对象(创建一个内部对象,将this指向该实例(_p1))
3、执行构造函数中的代码,在构造函数中设置属性和方法(其中,操作this的部分就是操作了该实例(_p1))
4、 返回了当前对象,返回值:
a、如果函数没有返回值(没有return语句),那么就会返回构造函数的实例(p1)
b、如果函数返回了一个基本数据类型的值,那么本次构造函数的返回值是该实例(_p1)
function Fn(){ return 5;}
var f1 = new Fn();
console.log(f1); // Fn {}
c、如果函数返回了一个复杂数据类型的值,那么本次函数的返回值就是该值,举例:
function fn3(){
return [1,3,5];
//数组是一个对象类型的值,
//所以数组是一个复杂数据类型的值
//-->本次构造函数的真正返回值就是该数组
//-->不再是fn3构造函数的实例
}
var f3=new fn3(); //f3还是fn3的实例吗?错
//f3值为[1,3,5]
三、对象的增删改查
js
var obj = {name: "Tom", age: 18};
obj.height = "180cm"; // 设置对象的属性
delete obj.age; // 删除对象的属性
obj.name = "Jerry"; // 修改对象的属性
console.log(obj); // {name:"Jerry", height: "180cm"}
1.获取/设置对象的属性
js
// 1、 对象.属性名
console.log(hero.name);
// 2、 对象['属性名']--推荐使用
console.log(hero['name']);
// 设置属性
hero.gender = "男"; // 添加一个不存在的属性
hero["gender"] = "male"; // 修改一个已存在的属性值
2.访问对象的方法
js
// 对象.方法名
hero.attack( );
3.遍历对象的方法
js
// 1. for in
语法: for (var 变量名 in 对象){} 变量名 = 对象的属性名
对象中有几个属性,循环体就会执行几次
var obj = { name:'孙悟空', age:30, gender:'boy',
skill:function () { alert('筋斗云'); } }
for (var key in obj) {
console.log(key + '------' + obj[key]);
}
四、this关键词
1.函数中的this指向
js
function fn() {
console.log(this);
}
fn(); //Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, ...}
2.对象方法中的this指向
js
对象的方法中:this 指向 调用该方法的当前对象
var obj = {
name: 'zs',
fn: function() {
console.log(this.name);
}
}
obj.fn(); // zs
3.构造函数中的this
js
在构造函数中:this 就是构造函数创建的对象
function Fn(){
this.name = 'zs';
this.age = 18;
console.log(this); // Fn {name: "zs", age: 18}
}
var obj = new Fn();
五、内存泄露
js
var p1={age:100,gender:"女",say:function(){}}
var p2={age:100,gender:"女",say:function(){}}
var p3={age:100,gender:"女",say:function(){}}
var p4={age:100,gender:"女",say:function(){}}
var p5={age:100,gender:"女",say:function(){}}
var p6={age:100,gender:"女",say:function(){}}
//上述代码,
//a、存在很多冗余代码
//b、所有的人对象都有say方法,并且功能相似,但是他们占据了不同的内存-->会导致内存浪费(内存泄漏)
六、构造函数
js
function Person(age,gender){
this.age=age;
this.gender=gender;
this.say=function(){ ... } //此时的内存依然浪费了-->原型
}
var p1=new Person(5,"未知"); //使用这种方式创建对象,代码整洁了很多
var p2=new Person(5,"未知"); //Person是p1的构造函数,p2是Person构造函数的实例
var p3=new Person(5,"未知");
var p4=new Person(5,"未知");
var p5=new Person(5,"未知");
var p6=new Person(5,"未知");
// 任何函数都可以当成构造函数 例: function CreateFunc(){ }
// 只要把一个函数通过new的方式来进行调用,我们就把这一次函数的调用方式称之为:构造函数的调用
// new CreateFunc(); 此时CreateFunc就是一个构造函数
// CreateFunc(); 此时的CreateFunc并不是构造函数
// 其它见上面创建对象的方式 - 自定义构造函数
七、js中继承的概念
js
function Person(name,age){
this.name=name;
this.age=age;
this.say=function(){}
}
var p1=new Person();
var p2=new Person();
//p1对象和p2对象的say方法是否是同一个方法:false
console.log(p1.say===p2.say);
js
// 由于say方法可能功能相似,但是不是同一个方法(没有指向同一块内存,会造成内存浪费)
// 解决方案:把say方法写在他们共同的(父对象)中
// 其实他们共同的父对象,就可以通过:Person.prototype来获取
//只要把say方法写在Person.prototype中,那么say方法就是同一个方法
Person.prototype.run=function(){ console.log('时速500KM');}
p1.run(); // 此时p1和p2都可以访问到run方法
p2.run();
// 验证p1.run和p2.run是否是同一个方法?
console.log(p1.run == p2.run); //指向同一个方法,这种方法避免了内存的浪费
console.log(p1.run == Person.prototype.run); // true
var p3=new Person();
console.log(p3.run == p1.run); //true
console.log(p3.run === p1.run);//true
// 结论:只要往某个构造函数的prototype对象中添加某个属性、方法,那么这样的属性、方法都可以被所有的构造函数的实例所共享
// 这里的【构造函数的prototype对象】称之为原型对象
// Person.prototype是 p1 p2 p3 的原型对象
// Person.prototype是Person构造函数的【实例】的原型对象
// 猜猜看?
// Person的原型对象是谁呢?
// -->首先要知道Person的构造函数:-->Function
// -->所以Person的原型对象是:Function.prototype
// p1的原型对象是谁呢?
// -->首先要知道p1是谁创建的? -->Person
// -->所以p1的原型对象时: Person.prototype
继承的实现方式
js
// 1. 原型链继承
function Dog(){} // 1.1 创建一个构造函数
var d1=new Dog(); // 1.2 通过new 构造函数得到 d1 实例
// d1.say=function(){} // 不行:错误写法
Dog.prototype.say=function(){ console.log('汪汪汪');} //正确写法:为了让d1有一个叫的方法
// 2. 原型链继承2
// a、一般情况下,先改变原型对象,再创建对象
// b、对于新原型,添加一个 constructor属性,从而不破坏原对象原型的结构
Person.prototype = {
constructor: Person,
say:function(){},
run:function(){}
}
// 3. 拷贝继承(混入继承)
var o3={gender:"男",grade:"初三",group:"第五组",name:"张三"};
var o4={};
//a、取出o3对象中的每一个属性
for (var key in o3) { //key就是o3对象中的每一个属性
var value = o3[key]; //b、获取到对应的属性值
o4[key] = value; //c、把属性值放到o4中
}
o4.name="李四" // 修改克隆对象,把该对象的name属性改为"李四",就不会影响到o3
console.log(o4); // 最终的目标对象的结果
//结论:后续如果修改了o4对象中的相关属性,就不会影响到o3