JS学习笔记 对象、包装类、原型、原型链、命名空间、对象枚举
程序员文章站
2022-03-17 13:12:41
...
文章目录
对象
对象是JavaScript的一个基本数据类型,是一种复合值,它将很多值(原始值或者其他对象)聚合在一起,可通过名字访问这些值。即属性的无序集合。
对象的创建
对象直接量/字面量
var obj = {
name : '守敬',
age : '18'
}
console.log(obj.name);//守敬
构造函数(重点)
系统自带构造函数
var obj = new object();
obj.name = '守敬';
console.log(obj.name);//守敬
自定义构造函数
为了和普通函数区分,构造函数采用大驼峰式命名法。
function Car(carName, color) {
this.name = carName;
this.color = color;
}
var car = new Car('BMW', 'red');
console.log(car.name);//BMW
console.log(car.color);//red
- 自定义构造函数内部原理
- 在函数体最前面隐式的加上this{}
- 执行this.xxx = xxx;
- 隐式的返回this(return)
原理解释:
注意隐士创建的this对象中有个名为__proto__
的属性,其属性值为该构造函数继承的原型prototype。
function Car(carName, color) {
//隐式创建this对象
//var this = {
// color : '',
// name : '',
// health : '',
// run = function (){
// this.health --;
// }
// };
this. color : color,
this.name : carName,
this.health : 100,
this.run = function () {
//对象方法
this.health --;
}
//return this;返回this对象
}
Object.create
此方法创建一个继承该原型的实例对象
留坑待补
对象的增删改查
- 增: 所谓增添一个对象的属性,就是直接对该属性进行赋值操作即可,这就相当于为该对象添加了一个新属性,而打印未添加的属性,浏览器不会报错,而是会打印出undefined
var obj = {};
console.log(obj.name); //undefined (不会报错)
obj.name = 'shoujing';
console.log(obj.name); // shoujing
- 删:我们通过delete操作符来删除一个对象的属性
var obj = {
name : 'shoujing'
}
console.log(obj.name); //shoujing
delete obj.name;
console.log(obj.name); //undefined
- 改: 修改一个对象的属性是最简单的了,直接通过赋值操作赋予其其他的值即可
var obj = {
name: 'shoujing'
}
console.log(obj.name); //shoujing
obj.name = 'obj';
console.log(obj.name); // obj
- 查:查询一个对象的属性值有两种方法
var obj = {
name: 'shoujing'
};
// 第一种方法
console.log(obj['name']); //shoujing
// 第二种方法
console.log(obj.name); //shoujing
//p.s.最本质的是第一种方法,因为在使用第二种方法时,后台自动将其转换为第一种字符串的形式来查询
p.s.以上的增、删、改三种操作都只是针对当前对象的属性进行操作,而不会影响到当前对象的原型的属性。而查询是先看看当前对象本身是否设置了该属性,如果当前对象未设置该属性,则再看该对象的原型中是否设置了该属性,若两者都没有,则返回undefined
包装类
-
五个原始值:number, string , boolean, undefined, null.其中number, string, boolean是分别拥有自己的包装类,而undefined和null是没有自己的包装类的
-
原始值不是对象,无法拥有自己的属性,但因为的包装类的存在,原始值就好似可以拥有自己的属性了,但其拥有的属性又有点特殊之处,如下用string来举例:
引例:
// str是string类型的,非对象,不能拥有属性,为什么能打印出 str.length?
var str = 'abcd';
console.log(str.length); //4
解释:
// 因为每次执行完一条完整js语句后该类型对象的包装类就会将该语句包装,所以也就不会导致报错了
var str = 'abcd';
// var str = new String('abcd');
console.log(str.length); //4
str.len = 4;
consloe.log(str.len);//undefinend
//length较为特殊,如果是自定义的len属性,则系统会进行如下过程:
//(new string('abcd')).len = 4; delete
//因此最后打印就是undefinde
举例
var str = 'abcd';
str.lenth = 2;
console.log(str); // ab or abcd ? answer is abcd
console.log(str.length); // 2 or 4 ? answer is 4
原型
- 原型的定义: 原型是function对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。原型也是对象。
- 利用原型特点和概念,可以提取共有属性。将一类对象的共有属性提取出来,放到该类对象的原型中,从而不需要每次用new操作符时都重新定义一遍该共有属性。
如下,定义一个Person构造函数,而属于Person多构造对象共有的属性方法,则定义到Person的原型中
Person.prototype = {
eat : function (food) {
console.log('I have eated ' + food);
},
sleep : function () {
console.log("I am sleeping");
}
}
// 人的构造函数
function Person (name, age) {
this.name = name;
this.age = age;
}
var person1 = new Person('shoujing', 18);
console.log(person1.name); //shoujing
person1.eat('apple'); //I have eated apple
- 如何查看原型:
- 之前是不允许我们查看构造函数的原型的,但后来提供了一个可查看构造函数原型的接口:隐士属性
__proto__
(其实我们能够访问原型的属性,或者说继承原型,靠的就是__proto__
属性连接着构造函数和原型,可以说没有__proto__
属性的存在,就无法实现原型的继承) - 首先我们先说明一下
__proto__
这个接口是存放到哪里的看过以上对象创建过程的都应该知道在用new创建一个对象时,内部会隐式自动创建一个this的对象,进过一系列处理后再隐士将this对象返回。而__proto__
就在隐士创建的this对象中,如下代码:
- 之前是不允许我们查看构造函数的原型的,但后来提供了一个可查看构造函数原型的接口:隐士属性
// 原型
Person.prototype = {
say: function () {
console.log("I am saying ");
},
play: function () {
console.log("I am playing");
}
}
// 构造函数
function Person (name) {
// var this = Object.create(Person.prototype);
// p.s.在隐士创建的this对象中存在一个属性,即__proto__,该属性存储了Person.prototype
this.name = name;
// return this;
}
// 对象的创建
var person1 = new Person('lyl');
// 打印原型
console.log(person1.__proto__);
- 如何查看对象的构造函数,我们通过属性constructor来查看:contructor属性位于构造函数的原型中,其中存储的是构造函数信息,所以在不知道原型的情况下,由原型继承原理,我们可以用实例对象来直接访问constructor,即获取创建该实例的构造函数。
function Person () {
this.name = 'myName';
this.age = 18;
}
var person = new Person();
console.log(person.constructor); // function Person(){...}
call、apply
call和apply的作用是改变构造函数中this的指向,二者唯一的区别在一参数传递的方式不同,如:
function Person (name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
function Student (name, age, sex, grade){
Person.call(this, name, age, sex)//call传参
Person.apply(this, [name, age, sex])//apply传参使用数组
this.grade = grade;
}
原型链
- 定义:顾名思义,原型链就是将一个个原型串连起来,形成一条原型继承的链子。
- 原型链的构成
如下代码例子, Son继承Father, Father继承Grand, 而Grand没有自定义原型,所以默认为原型链的最顶端new Object();为什么Object为最顶端,因为Object.prototype为null,为null是没有原型的。
//原型链 Son --> new Father() --> new Grand() --> new Object();
function Grand () {
}
Father.prototype = new Grand();
function Father () {
}
Son.prototype = new Father();
function Son () {
}
var son = new Son();
console.log(son);
原型链上属性的增删改查
以上述原型链为例说明:Son --> new Father() --> new Grand() --> new Object()
- 增
为son实例对象添加属性,总是添加为其自己本身的属性,为对原型和原型链是没有影响的。(再具体说即对和其相同构造函数构造的实例对象无法造成影响,以下说法同此) - 删
使用delete
操作符只能删除son实例对象自己本身的属性,而无法删除由原型继承而来的属性. - 改
- 若修改的属性为继承自原型的,且值类型为原始值,则仅仅修改的是该实例对象的属性,对原型无法造成影响。
- 若修改的属性为继承自原型的,属性值类型为引用值,则对引用值的修改又分两种情况
- 是直接对该修改的属性赋值 => 此情况仅仅修改的是实例对象的该属性,无法对原型造成影响
- 对该修改的属性添加内容或去除内容,而不是对其重新赋值 => 此情况会对原型造成影响。如下例
Person.prototype = {
has : [1, 2, 3]
}
function Person () {
this.name = 'shoujing';
}
var person1 = new Person();
var person2 = new Person();
person1.has.push(4);
//person1 和 person2都改变了,因为person1的修改影响到了原型,进而影响到了另一个实例对象
console.log(person1.has); //[1, 2, 3, 4]
console.log(person2.has); // [1, 2, 3, 4]
- 查
查询过程如下,首先看构造函数中是否有要查询的属性,若有,则直接返回,若没有,则看其原型有没有要查询的属性,若没有,则再看原型的原型上是否有要查询的属性,以此顺序在原型链上查询,若一直到原型链顶端后仍没有要查询的属性,则返回undefined
绝大多数对象最终都会继承自Object.prototype
null,和undefined是没有原型的
对象继承史
传统模式
- 即正常的通过构造函数创建实例对象,来继承原型–>原型链
- 缺点:继承了过多不必要的属性
借用构造函数
- 原理是通过call/apply改变构造函数内this对象的引用
- 不能继承借用构造函数的原型
- 每次构造函数都要多走一个函数
- 举例
function Person (name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
function Student (name, age, sex, grade){
Person.call(this, name, age, sex)//call传参
//Person.apply(this, [name, age, sex])//apply传参使用数组
this.grade = grade;
}
var student = new Student('张三', 18, 'male', 2107);
console.log(student);
共享原型
- 即将其他构造函数的原型直接赋值给本构造函数的原型
- 两个原型会相互影响,更改其中一个原型,另一个对应的原型也会被更改。
- 举例
Person.prototype = {
name : 'shoujing',
age : 18,
sex : 'male'
}
function Person () {
this.hooby = 'sing';
}
Student.prototype = Person.prototype;
function Student() {
this.grade = 2017;
}
Son.prototype = Person.prototype;
function Son () {
this.grand = 'luo';
}
var person = new Person();
var student = new Student();
var son = new Son();
console.log(person.age);//18
console.log(student.age);//18
console.log(son.age);//18
//原型继承成功
Student.prototype.age = 20;//更改其中一个原型的age属性
console.log(student.age);//20
console.log(son.age);//20
//继承Person原型的Student和Son相互影响
圣杯模式
每次继承的都是新创建的F构造函数实例,相互之间不会影响。其实此处针对F形成了闭包,Target引用了F,导致F不会销毁。
正常函数形式:
function inherit (Target, Origin) {
// 借用F这个中间量来继承,而不是直接共享原型
var F = function (){}
F.prototype = Origin.prototype;
Target.prototype = new F();
// 自定义构造函数原型时,同时要更正自定义原型的constructor,否则一般默认为Object(),次函数若不指定constructor,则constructor为Origin
Target.prototype.constructor = Target;
Target.prototype.uber = Origin; //记录真正继承的是谁
}
闭包形式(推荐,YUI)
var inherit = (function(){
var F = function (){};
return function (Target, Origin) {
F.prototype = Origin.prototype;
Target.prototype = new F();
Target.prototype.constructor = Target;
Target.prototype.uber = Origin;
}
})();
命名空间
我们可以利用对象(闭包)创建命名空间来管理变量,防止污染全局,适用于模块开发,举个例子:
var workSpace = {
person1: {
name: 'one',
age: 18
},
person2: {
name: 'two',
age: 20
}
}
// 这样两个人虽然有同名变量,但不会相互影响,因为位于不同命名空间
// 访问第一个人的姓名
console.log(workSpace.person1.name); // one
console.log(workSpace.person2.name); //two
对象枚举
- prop in obj
var obj = {
name : '123',
age : 18,
sex : 'male',
height : 180,
weight : 75,
}
for (var prop in obj) {
console.log(obj[prop]);
}
- obj.hasOwnProperty(‘prop’);
- 该方法的作用是来判断对象obj的自身属性中是否含有属性prop,自身属性是在构造函数中生成的或者实例对象后来自己添加的,而继承属性则是从原型上继承的属性,所以该方法就是判断该属性是从原型继承来的还是自身的。返回结果为true,则为自身属性。
var obj = {
name : '123',
age : 18,
sex : 'male',
height : 180,
weight : 75,
__proto__ : {
lastName : 'deng'
}
}
for (var prop in obj) {
if(obj.hasOwnProperty(prop)) {
console.log(obj[prop]);
}
}
- object instanceof Object;
- instanceof操作符用来判断object实例对象是否为Object构造函数创建的,举例
Person.prototype.age = 18;
function Person () {
this.name = 'lyl';
}
var person = new Person();
console.log(person instanceof Person); // true
console.log(new Object() instanceof Person); //false