JS高级:面向对象解析
1 实例属性/方法
都是绑定在使用构造函数创建出来的对象p上; 最终使用的时候也是使用对象p来进行访问;
function Person(name, age, doFunc) {
this.name = name;
this.age = age;
this.doFunc = doFunc;
}
var p1 = new Person('sz', 18, function () {
console.log('sz在上课');
});
var p2 = new Person('王二小', 18, function () {
console.log('王二小在放羊');
});
2 静态属性/方法
函数本质也是一个对象, 既然是个对象, 那么就可以动态的添加属性和方法
只要函数存在, 那么绑定在它身上的属性和方法, 也会一直存在
eg,记录总共创建了多少个人对象:
2.1 全局变量
// 1. 设置一个全局的变量
var personCount = 0;
function Person(name, age, doFunc) {
this.name = name;
this.age = age;
this.doFunc = doFunc;
personCount++;
}
var p1 = new Person('sz', 18, function () {
console.log('sz在上课');
});
var p2 = new Person('王二小', 18, function () {
console.log('王二小在放羊');
});
console.log('总共创建了'+ personCount + '个人'); //2
2.2 静态属性/方法
function Person(name, age, doFunc) {
this.name = name;
this.age = age;
this.doFunc = doFunc;
if (!Person.personCount) {
Person.personCount = 0; //创建静态属性
}
Person.personCount++;
}
//创建静态方法
Person.printPersonCount = function () {
console.log('总共创建了'+ Person.personCount + '个人');
};
var p1 = new Person('sz', 18, function () {
console.log('sz在上课');
});
var p2 = new Person('王二小', 18, function () {
console.log('王二小在放羊');
});
Person.printPersonCount();
3 类型获取
3.1 内置对象类型获取
内置对象
• String
• Number
• Boolean
• Object
• Function
• Array
• Date
• RegExp
• Error
获取根据自己声明的构造函数创建的对象
console.info("-->str")
var str = "aaa";
console.log(typeof str); //string
console.log(str.constructor.name); //String
console.log(Object.prototype.toString.call(str)); //[object String]
console.info("-->obj")
var obj = { 'name': '张三' };
console.log(typeof obj); //object
console.log(obj.toString()); //[object Object]
console.log(obj.constructor.name); //Object
console.log(Object.prototype.toString.call(obj)); //[object Object]
console.info("-->arr")
var arr = [1, 2, 3];
console.log(typeof arr); //object
console.log(arr.toString()); //1,2,3
console.log(arr.constructor.name); //Array
console.log(Object.prototype.toString.call(arr)); //[object Array]
console.info("-->date")
var date = new Date();
console.log(typeof date); //object
console.log(date.toString()); //Wed Oct 09 2019 00:08:15 GMT+0800 (中国标准时间)
console.log(date.constructor.name); //Date
console.log(Object.prototype.toString.call(date)); //[object Date]
3.2 自定义类型获取
还是使用 实例化对象.constructor.name
function Person(name, age) {
// var this = new Object(); 自定义类型系统都把this指向Object,所以Object.prototype.toString.call(...) 获取都是 [object Object]
this.name = name;
this.age = age;
}
function Dog(name, age) {
this.name = name;
this.age = age;
}
function Cat(name, age) {
this.name = name;
this.age = age;
}
// 1. 实例化-->实例
var p = new Person('zs', 18);
var d = new Dog('小花', 8);
var c = new Cat('小猫', 3);
// object
console.log(typeof p);
console.log(typeof d);
console.log(typeof c);
// [object Object]
console.log(p.toString());
console.log(d.toString());
console.log(c.toString());
// [object Object]
console.log(Object.prototype.toString.call(p));
console.log(Object.prototype.toString.call(d));
console.log(Object.prototype.toString.call(c));
console.log(p.constructor.name); //Person
console.log(d.constructor.name); //Dog
console.log(c.constructor.name); //Cat
3.3 类型验证 instanceof
类型验证使用 instanceof
function Person(name, age) {
this.name = name;
this.age = age;
}
function Dog(name, age) {
this.name = name;
this.age = age;
}
function Cat(name, age) {
this.name = name;
this.age = age;
}
// 1. 实例化-->实例
var p = new Person('zs', 18);
var d = new Dog('小花', 8);
var c = new Cat('小猫', 3);
//true
console.log(p instanceof Person);
console.log(d instanceof Dog);
console.log(c instanceof Cat);
//true
console.log(p instanceof Object);
console.log(d instanceof Object);
console.log(c instanceof Object);
//false
console.log(p instanceof Dog);
console.log(d instanceof Cat);
console.log(c instanceof Person);
console.log(p);
console.log(d);
console.log(c);
//true
console.log([] instanceof Object);
4 访问函数原型对象
function Person(name, age) {
this.name = name;
this.age = age;
}
// 原型对象
Person.prototype.run = function () {
console.log('跑');
};
4.1 方式一:函数名.prototype
console.log(Person.prototype);
浏览器
> {run: ƒ, constructor: ƒ}
> run: ƒ ()
> constructor: ƒ Person(name, age)
> __proto__: Object
4.2 方式二:对象.proto(不推荐)
.__proto__看起来很像一个属性,但是实际上它更像一个getter/setter
var p = new Person();
console.log(p.__proto__);
浏览器
> {run: ƒ, constructor: ƒ}
> run: ƒ ()
> constructor: ƒ Person(name, age)
> __proto__: Object
__proto__
是一个非标准属性
即ECMAScript中并不包含该属性,这只是某些浏览器为了方便开发人员开发和调试而提供的一个属性,不具备通用性
建议:在调试的时候可以使用该属性,但不能出现在正式的代码中
4.2.1 __proto__可以设置
.__proto__
是可设置属性,可以使用ES6的Object.setPrototypeOf(..)进行设置。然而,通常来说你不需要修改已有对象的[[Prototype]]。
var newYX = {
'add': function () {
console.log('sum');
}
};
p.__proto__ = newYX;
console.log(p.__proto__);
浏览器
> {add: ƒ}
> add: ƒ ()
> __proto__: Object
5 判断原型对象是否存在某属性
5.1 in 属性
in 判断一个对象, 是否拥有某个属性(如果对象身上没有, 会到原型对象里面查找)
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.address = '上海';
var p = new Person('撩课', 18);
// console.log(name in p); // false
console.log('name' in p); // true
console.log('address' in p); // true
5.2 hasOwnProperty 属性
只到对象自身查找
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.address = '上海';
var p = new Person('张三', 20);
console.log(p.hasOwnProperty('name')); // true
console.log(p.hasOwnProperty('address')); // false
6 判断一个对象的原型 isPrototypeOf 和 instanceOf
是的,它们执行相同的操作,都遍历原型链以查找其中的特定对象。
两者的区别在于它们是什么,以及如何使用它们,例如isPrototypeOf
是对象上可用的函数。它允许您测试一个特定对象是否在另一个对象的prototype
链中,因为此方法是在object
上定义的原型,它对所有对象都可用。
instanceof
是一个操作符,它需要两个操作数,一个对象和一个构造函数,它将测试传递的函数原型属性是否存在于对象链上(通过[[HasInstance]](V)
内部操作,仅在函数对象中可用)。
B.isPrototypeOf(a) 判断的是A对象是否存在于B对象的原型链之中,检查B
a instanceof B 判断的是B.prototype是否存在与A的原型链之中,检查B.prototype
isPrototypeOf() 与 instanceof 运算符不同。在表达式 "object instanceof AFunction"中,object 的原型链是针对 AFunction.prototype 进行检查的,而不是针对 AFunction 本身。
function A () {
this.a = 1;
}
function B () {
this.b = 2;
}
B.prototype = new A();
B.prototype.constructor = B;
function C () {
this.c = 3;
}
C.prototype = new B();
C.prototype.constructor = C;
var c = new C();
// instanceof expects a constructor function
c instanceof A; // true
c instanceof B; // true
c instanceof C; // true
// isPrototypeOf, can be used on any object
A.prototype.isPrototypeOf(c); // true
B.prototype.isPrototypeOf(c); // true
C.prototype.isPrototypeOf(c); // true
7 继承(MDN)
7.1 定义 Teacher() 构造器函数
function Person(first, last, age, gender, interests) {
this.name = {
first,
last
};
this.age = age;
this.gender = gender;
this.interests = interests;
};
Person.prototype.greeting = function() {
console.log('Hi! I\'m ' + this.name.first + '.');
};
function Teacher(first, last, age, gender, interests, subject) {
Person.call(this, first, last, age, gender, interests); //用的是传送给Teacher(),而不是Person()的值
this.subject = subject;
}
var p = new Person("张","三",22,"男",["篮球","KTV"]);
var t = new Teacher("李","四",18,"男",["英语","计算机"],"编程");
t.greeting();
//TypeError: t.greeting is not a function
//这里 Teacher 不能获取 greeting...
这里主要调用Person.call(this, first, last, age, gender, interests);
定义 Teacher() 构造器函数,
我们很有效的在Teacher()构造函数里运行了Person()构造函数,得到了和在Teacher()里定义的一样的属性,但是用的是传送给Teacher(),而不是Person()的值(我们简单使用这里的this作为传给call()的this,意味着this指向Teacher()函数)。
7.2 设置 Teacher() 的原型
我们需要让Teacher()从Person()的原型对象里继承方法
Teacher.prototype = Object.create(Person.prototype);
在这个例子里我们用这个函数来创建一个和Person.prototype一样的新的原型属性值(这个属性指向一个包括属性和方法的对象),然后将其作为Teacher.prototype的属性值。
7.2.1 为什么不能使用 Teacher.prototype = Person.prototype
看看比较
7.3 设置 Teacher() 构造器引用
现在Teacher()的prototype的constructor属性指向的是Person(), 这是由我们生成Teacher()的方式决定的。
这或许会成为很大的问题,所以我们需要将其正确设置——您可以回到源代码,在底下加上这一行代码来解决:
Teacher.prototype.constructor = Teacher;
注:每一个函数对象(Function)都有一个
prototype
属性,并且只有函数对象有prototype
属性,因为prototype
本身就是定义在Function
对象下的属性。当我们输入类似var person1=new Person(...)
来构造对象时,JavaScript实际上参考的是Person.prototype
指向的对象来生成person1
。另一方面,Person()
函数是Person.prototype
的构造函数,也就是说Person===Person.prototype.constructor
(不信的话可以试试)。在定义新的构造函数
Teacher
时,我们通过function.call
来调用父类的构造函数,但是这样无法自动指定Teacher.prototype
的值,这样Teacher.prototype
就只能包含在构造函数里构造的属性,而没有方法。因此我们利用Object.create()
方法将Person.prototype
作为Teacher.prototype
的原型对象,并改变其构造器指向,使之与Teacher
关联。任何您想要被继承的方法都应该定义在构造函数的
prototype
对象里,并且永远使用父类的prototype
来创造子类的prototype
,这样才不会打乱类继承结构。
new 与 Object.create 区别
- new的话只能是class(即函数),但是Object.create()的参数可以为对象也可以为函数,
- 如果Object.create()的参数是对象的话,那么新的对象会继承原对象的属性;如果参数是类(函数)的话,Object.create()的参数为类(函数)原型,如您所说的,它没有绑定this,并没有属性被继承。
- Object.create(null)可以实现一个空对象,即没有原型的对象,但使用new就办不到。
7.4 多对象继承
Object.assign(目标对象, ...源对象)
方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
//再创建一个基类
function Animal(age) {
this.age = age;
}
Animal.prototype.say = function(language) {
console.log('you say ' + language);
}
function Student(name, sex, age) {
Person.call(this, name, sex);
Animal.call(this, age);
}
//原型链拼接
Student.prototype = Object.create(Person.prototype);
Object.assign(Student.prototype, Animal.prototype);
Student.prototype.constructor = Student;
Student.prototype.getInfo = function() {
console.log('getInfo: [name:' + this.name + ', sex:' + this.sex + ', age:' +this.age + '].');
};
var s = new Student('coco', 'femal', 25);
7.5 JS继承练习
function Animal(name, age) {
this.name = name;
this.age = age;
}
Animal.prototype.eat = function () {
console.log('吃');
};
Animal.prototype.run = function () {
console.log('跑');
}
function Person(name, age, job) {
Animal.call(this, name, age);
this.job = job;
}
Person.prototype = Object.create(Animal.prototype);
Person.prototype.constructor = Person;
//Person.prototype.jump 原型方法必须写在继承后
Person.prototype.jump = function () {
console.log('跳');
};
function Student(name, age, job, className) {
Animal.call(this, name, age);
Person.call(this, name, age, job);
this.className = className;
}
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
//原型方法必须写在继承后
Student.prototype.study = function () {
console.log('学习');
}
function Adolescent(name, age, job, className,sex) {
Animal.call(this, name, age);
Person.call(this, name, age, job);
Person.call(this, name, age, job,className);
this.sex = sex;
}
Adolescent.prototype = Object.create(Student.prototype);
Adolescent.prototype.constructor = Adolescent;
//原型方法必须写在继承后
Student.prototype.play = function () {
console.log('跳绳');
}
this.a2 = new Adolescent('张同学',18,'学生','高2.1版',"女");
this.s1 = new Student('张同学',18,'学生','高2.1版');
this.p1 = new Person('王老师',26,'教师');
this.a1 = new Animal('校长',44);
原型方法必须写在设置原型 XXX.prototype = Object.create(...)
和设置构造器引用XXX.prototype.constructor = xxx
之后,不容会丢失。
7.6 内置对象的扩展
方式1(失败)
直接给对象动态添加属性和方法
弊端:
如果操作很多个对象, 则无法共享
代码比较冗余
例子:arr2的run不能执行~
var arr = [1,2,3];
arr.run = function () {
console.log('跑');
}
arr.run();
var arr2 = [2];
arr.run = function () {
console.log('跑');
}
方式2(容易覆盖)
直接给Array原型对象添加方法
弊端:
可能会产生覆盖的情况
例子:覆盖push
Array.prototype.push = function () {
console.log('跑');
};
var arr = [1,2,3];
arr.push();
var arr2 = [2];
arr2.push();
方式3(推荐)
提供一个新的构造函数
修改构造函数的原型指向为数组的原型对象
为了能够获取数组里面的属性和方法
问题:
依然会修改数组原型对象内容
优化
原型对象就是一个对象
可以直接根据Array创建一个对象, 给新创建的函数原型对象进行赋值
function MyArray() {}
MyArray.prototype = new Array();
MyArray.prototype.run = function () {
console.log('跑');
};
var arr = new MyArray();
arr.run();
8 深拷贝与浅拷贝
8.1 浅拷贝
8.1.1 遍历
var obj1 = {name: '张三', age: 18};
var obj2 = {};
for (var key in obj1) {
obj2[key] = obj1[key];
}
console.log(obj2);
8.1.2 Object.assign(目标对象, ...源对象)
方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
var obj3 = {className: '六班'};
Object.assign(obj3, obj1, {address: '重庆'});
console.log(obj3);
具有相同的
键
,则属性将被源对象
中的属性覆盖
const target = { a: 1, b: 3 };
const source = { b: 4, c: 5 };
const returnedTarget = Object.assign(target, source);
console.log(returnedTarget);
// expected output: Object { a: 1, b: 4, c: 5 }
8.2 深拷贝
深拷贝/浅拷贝
区别:在于对引用值的处理
- 浅拷贝, 直接拷贝一份地址
- 深拷贝-拷贝地址对应的具体内容
深拷贝实现
- 提供一个函数,两个参数(元对象,要拷贝属性的对象)
- 在函数中先检查第一个参数是否有值,如果没有值那么就初始化一个空的对象
- for..in循环来变量参数2
- 检查当前的属性值是什么类型
- 如果是值类型,那么就直接拷贝赋值
- 如果是引用类型,那么就再调用一次这个方法,去内部拷贝这个对象的所有属性
var obj = {
name: "唐三三",
age: 30,
friend: ['张森', '夏赢'],
address: {
city: '重庆',
county: '渝北'
},
play: function () {
console.log(this.name + '喜欢桌球');
}
}
//deep Copy
function deepCopySource2Target(source, target) {
for (var key in source) {
var sourceValue = source[key];
if (!(sourceValue instanceof Object)) {
//value object
target[key] = sourceValue;
}
else {
//object object
var temp = new sourceValue.constructor;
deepCopySource2Target(sourceValue, temp);
target[key] = temp;
}
}
}
var newObj = {};
deepCopySource2Target(obj, newObj);
console.log(newObj);
newObj.play();
参考:
js继承实现之Object.create - 太阳的眼睛
javascript中继承-MDN
继承与原型链-MDN
ObjectPlayground.com - 一个非常有用的、用于了解对象的交互式学习网站。
彻底搞懂JavaScript中的继承- 掘金
JS高级:面向对象解析的更多相关文章
- js高级-面向对象继承
一.工厂模式创建对象及优缺点 继承就是把公共的部分抽象出来作为父类,基类.吃饭,跑步等 var a = {}; //批量创建不方便,不能重复设置公共属性的代码 //工厂模式出现了,创建10个Cat对象 ...
- JS高级 - 面向对象5(继承,引用)
<script type="text/javascript"> //------------------Person类 //(Person)的构造函数 function ...
- JS高级 - 面向对象1(this,Object ,工厂方式,new )
面向对象三要素: 封装 继承 多态 1.this 详解,事件处理中this的本质 window this -- 函数属于谁 <script type="text/javascript& ...
- JS高级---面向对象的编程思想(贪吃蛇梳理)
面向对象的编程思想(贪吃蛇梳理) 模拟贪吃蛇游戏,做的项目 地图: 宽,高,背景颜色,因为小蛇和食物都是相对于地图显示的, 这里小蛇和食物都是地图的子元素, 随机位置显示, 脱离文档流的, 地图也需要 ...
- JS高级 - 面向对象4(json方式面向对象)
把方法包在一个Json里 var p1 = { name: "唐三", sex: "男", dreamdu: { URL: "www.dreamdu. ...
- JS高级 - 面向对象3(面向过程改写面向对象)
改写: 1.前提:所有东西都在 onload 里 2.改写:不能有函数嵌套,可以有全局变量 onload --> 构造函数 全局变量 --> 属性 函数 --> 方法 4.改错: t ...
- JS高级 - 面向对象2(prototype定义)
定义和用法 prototype 属性允许您向对象添加属性和方法 注意: Prototype 是全局属性,适用于所有的Javascript对象. 语法 object.prototype.name=val ...
- JS高级——面向对象方式解决tab栏切换问题
注意事项 1.给li元素注册事件,函数里面的this指的li元素,那么我们可以在注册事件之前将Tab对象用that=this进行保存 2.使用沙箱模式,所以暴露给外面的变量使用的是window.tab ...
- JS高级——面向对象方式解决歌曲管理问题
需要注意的问题: 1.其他模块若是使用构造函数MP3创建对象,唯一不同的就是他们传入的音乐库是不一样的,所以构造函数中存在一个songList属性,其他一样的就被添加到了构造函数的原型对象之中 2.原 ...
- 《JS高级程序设计》笔记 —— 解析查询字符串
今天在继续翻阅<JS高级程序设计>的时候,正好翻到location对象这一小节,其中有一部分就是讲的解析查询字符串.看到这个内容立马想到了做去哪儿秋招笔试题的时候有这么一道题. 去哪儿笔试 ...
随机推荐
- CentOS7安装MySQL5.7及Tomcat8.5
在CentOS7服务器上部署FR项目应用 一.安装CentOS-7_x86_64 1.CentOS7:带GUI的服务器(FTP服务器.JAVA平台.兼容性程序库.开发工具.安全性工具.系统管理工具): ...
- python的推导式 —— 列表推导式、集合和字典推导式
python的推导式是用于快速处理数据的方法. 主要有:列表推导式.集合推导式和字典推导式 import time import numpy as np 列表推导式: 1. 速度快 t1 = time ...
- admin端的教师管理功能测试
1 概述 1.1 测试范围 本次所测试的内容是admin端的教师管理功能. 1.2 测试方法 采用黑盒子方法进行集成测试. 1.3 测试环境 (1) 服务器l 操作系统:Windo ...
- jdbc.DataSourceProperties$DataSourceBeanCreationException: Failed to determine a suitable driver class
java.lang.IllegalStateException: Failed to load ApplicationContext at org.springframework.test.conte ...
- Cocos2d-x学习小结 配置篇
Cocos2d-x学习小结 配置篇 学习工具:Cocos2d-x用户手册,<Cocos2d-x游戏开发之旅> 首先官网下载cocos2d-x源码,安装vs2019.如果没有安装python ...
- C# 跨线程对控件赋值
第一种 跨线程对控件赋值 private void button2_Click(object sender, EventArgs e) { Thread thread1 = new Thread(ne ...
- 持续集成学习4 jenkins常见功能
一.节点选择 1.yum安装jdk yum install -y java-1.8.0 java-1.8.0-openjdk-devel 2.节点选择有三种方式 a.通过系统自带功能限制任务只能在这个 ...
- 微信小程序开发工具“当前系统代理不是安全代理”
(1)删除注册表中以proxy开头的项目再次重启 regedit进入[HKEY_CURRENT_USER/Software/Microsoft/Windows/CurrentVersion/Inter ...
- terraform v0.12.0 发布了
v0.12.0 相比以前的有好多新的特性,包括语法,以及函数增强,昨天还在折腾的一个json解码的问题,直接使用 v0.12.0 就可以解决了,同时也包含了for 操作处理同时官方文档对于v0.12. ...
- bg/fg
将一个在后台暂停的命令,变成继续执行 (在后台执行). 一般ctrl+z就把前台命令调到了后台 将后台中的命令调至前台继续运行