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对象这一小节,其中有一部分就是讲的解析查询字符串.看到这个内容立马想到了做去哪儿秋招笔试题的时候有这么一道题. 去哪儿笔试 ...
随机推荐
- Java开发环境之RabbitMQ
查看更多Java开发环境配置,请点击<Java开发环境配置大全> 捌章:RabbitMQ安装教程 1)下载安装Erlang 官网下载:http://www.erlang.org,有时比较难 ...
- linux绝大部分命令集合(自己需要的时候方便查找)
原网页:https://www.linuxidc.com/Linux/2018-04/151726.htm 系统信息显示命令arch 显示机器的处理器架构(1) uname -m 显示机器的处理器架构 ...
- svn: E155004: Working copy '/data/www' locked.
svn: run 'svn cleanup' to remove locks (type 'svn help cleanup' for details) svn: E155004: Working c ...
- ReqMan — 需求提取和协同处理工具
ReqMan是由德国engineering method AG公司开发的一款高效的.可自由定制的需求提取和协同处理工具.ReqMan 能够将PDF.Word.Excel等格式的文档提取 ...
- jmeter脚本中请求参数获取的几种方式
a.从数据库获取: 譬如接口请求参数中id的值,我需要从数据库获取,如下设置: 先设置jdbc connection configuration,然后设置JDBC b.从CSV获取: 获取CSV文件 ...
- element ui 中的 resetFields() 报错'resetFields' of undefined
每次做各种form表单时,首先要注意的是初始化,但是刚开始若没有仔细看文档,则会自己写个方法将数据设置为空,但是这样就会出现一个问题,表单内存在各种验证,假如是一个弹框内有form表单,弹框出现就执行 ...
- CentOS6.5配置
关闭防火墙 查看防火墙状态 /etc/init.d/iptables status 停止 /etc/init.d/iptables stop 开机不启动 chkconfig iptables off ...
- spark-scala开发的第一个程序WordCount
package ***** import org.apache.spark.{SparkConf, SparkContext} object WordCount { def main(args: Ar ...
- pycharm激活2018(终极解决办法)
1.将“0.0.0.0 account.jetbrains.com”中的内容添加到hosts文件中,hosts路径为:C:\Windows\System32\drivers\etc 2.打开http: ...
- npm run dev 报错 iview TypeError [ERR_INVALID_CALLBACK]: Callback must be a function
运行npm run dev报这个错 然后找到 D:\text\vue\iview-admin\build\webpack.dev.config.js打开 将这一行代码: fs.write(fd, bu ...