后端重新学习前端第五天

134 阅读4分钟

对象和包装类

对象有两种生命方式,字面量形式和实例化的方式,平常最常用的就是字面量形式。

var obj = {}; // 字面量形式
var obj = new Object(); // 实例化方式

对象属性的增删改查:

var obj = {
  name: 'zhangsan'
};

// create
obj.gender = 'male';

// read
obj.name;

// update
obj.name = 'lisi';

// delete
delete obj.gender;

自定义方法实例化对象,每一个对象都是独一无二的

function Person() {
  
}


var person = new Person(); 

对象命名规范:大驼峰式命名规则(函数是小驼峰式命名规则)

对象实例化发生的事

在使用new操作符之后

1、在函数体最前面隐式加上this = {}

2、执行this.xxx = xxx的赋值语句

3、隐式返回this

function Person(name) {
  // var this = {};
  this.name = name;
  
  // return this;
}

按照这样的逻辑,以下方式也能模拟实现new操作符。

function Person(name) {
  var that = {};
  
  that.name = name;
  
  return that;
}

对于一个使用new关键字实例的对象,会忽略其内部return的值。例如

function Person(name) {
  
  this.name = name;
  
  return 1111;
}

var person = new Person('111'); // person => {name: '111'}

包装类

总所周知,原始值是没有方法和属性的,但是我们平时却能够为原始值添加属性,例如:

var num = 4;
num.abc = 'sad';
console.log(num.abc); // undefined

上述给原始值添加属性并访问的操作实际上可以分为两个步骤

new Number(num).abc = 'sad'
console.log(new Number(num).abc)

对于字符串访问其length属性一样

var str = '1234';console.log(str.length); // 这一步操作实际上是 new String(str).length

原型

原型可以理解为对象的祖先,祖先具有的,儿子、孙子也能继承。

定义为:原型是function对象的一个属性,它定义勒构造函数制造出的函数的公有祖先。通过该构造函数产生的对象,可以集成原型上的属性和方法,原型也是对象

function Person(){
    
}
Person.prototype.firstName = 'zhang';
var person = new Person();
person.firstName // zhang

原型的增删改查:

Person.prototype.lastName = 'san';
function Person(firstName) {
    this.firstName = firstName;
}
var person = new Person('zhang');

// create,这里的创建是给对象创建一个属性而在原型上创建属性
person.gender = 'male'

// create,这里的创建是给原型增加一个属性
person.__proto__.gender = 'female'

// delete,delete仅能删除自身和自身原型的属性
delete person.firstName

// update
person.firstName = 'female'
person.__propto__.lastName = 'zhangsan'

// read
person.firstName
person.__proto__.fistName

对象访问属性规则:如果自身未查找到则向原型链上找,直到找到该属性或到object.__proto__为止。

function Person() {}
Person.prototype.name = 'zhangsan'
var person = new Person();
Person.prototype = {
    name: "lisi",
};

上述和如下例子类似

var obj = {name: 'a'}
var obj1 = obj;
obj = {name: 'b'}

所以person.name = 'zhangsan'

另外,此种情况也不一样

function Person() {}
Person.prototype.name = 'zhangsan'
Person.prototype = {
    name: "lisi",
};
var person = new Person();

由于Person.prototype提前变更了引用,所以person.name='lisi'

原型链

function Grand() {
}
Grand.prototype.lastName = 'zhangsan'
var grand = new Grand()

function Father() {
    this.firstName = 'lisi'
}
Father.prototype = grand;
var father = new Father();

function Son() {
    this.hobbit = 'trip'
}

Son.prototype = father;
var son = new Son();

console.log(son);

由原型可得知,如果在当前对象找不到属性,就会沿着其protoyoe=一直寻找该属性知道找到或到头位置。所以这里son.lastName实际上是son.__proto__.__proto__.__proto__.lastName,即grand.__proto__.lastName

这样,把一个一个的原型串成链就构成了原型链。

对于什么时候到头呢,一般情况下直到找到Object.prototype为止(特殊情况:Object.create(null)没有原型)

function Person() {
    this.name = 'zhangsan'
}
Person.prototype = {
    name: '111',
    say: function () {
        console.log(this.name);
    }
}
var p = new Person();
p.__proto__.say();
p.say();

此种情况,根据原型链属性查找规则,第一个将查找prototype里的name进行打印,而第二种情况由于person本身就有name,所以将会直接打印person自己的name。由此看来可以总结出一个规律,在不改变this的指向下,谁调用的this就是谁的当然自己如果找不到还是会去找原型链

Object.create(obj):可以创建以参数obj为原型的对象,当objnull时,创建出的对象无prototype

在基础类型的数据中仅有undefinednull没有原型

对于基础数据类型中toString等函数如果重写了这些函数,当调用时就不会调用其原型链的方法了

call、apply

callapply作用都是改变this指向。区别在于传参方式不同:apply参数传递数组,call挨个传递

function Person(name) {
    this.name = name;
    this.say = function (a, b, c) {
        console.log(this.name);
    }
}
var person = new Person('lisi');
var obj = {
    name: 'zhangsan'
};
person.say.call(obj, '111', '2232', '3333'); // zhangsan
person.say.call(person); // lisi
person.say.apply(obj, ['111', '2232', '3333']); // zhangsan

可以使用callapply借用其他的构造函数实现自己的需求

function Person(name, age) {
    this.name = name;
    this.age = age;
}
function Student(name, age, grade, tel) {
    Person.call(this, name, age);
    this.grade = grade;
    this.tel = tel;
}
var stu = new Student("张三", 17, 1, "1111");
console.log(stu);