JS从0开始(十一)原型链深入、对象继承、create、call和apply

325 阅读4分钟

平时没注意的常识:不写return的情况下: 普通函数默认返回值是undefined
构造函数 实例化以后 默认返回this

一.原型链

沿着_proto_ 往上去找 原型的属性值的链条,称之为原型链
也就是说自己没有的属性,会到原型链里面去找祖先的属性
但是父辈不能找子辈的属性,这就是原型链的继承:

2.原型链的终点(面试基础)

原型链的终点是: Object.prototype

此时构造器指向系统自带的Object

2.1 原型的原型

前两种写法直接生成对象,构造器都指向系统自带的 原型链顶端的Object;
第三种使用自定义构造函数,此时构造器 指向 自定义的构造函数

原型的原型的构造器一定指向系统自带的Object
这样就能证明原型的原型是由系统自带的Object构造出来的:

3.通过原型链增加 父元素中的属性(面试基础)

给子元素赋值会改变父元素中的值

同时也能更改父元素中属性的值(仅仅引用值能改),但是不推荐使用这种方式去修改父元素的值

4.父辈中原始值属性不能更改(面试基础)

父辈中的原始值属性不能更改:

总结:父辈中:原始值不会被改变,引用值能被改变,改变方(如上式的student)都会被改变

5. this指向问题(面试基础)

谁调用this,this就指向谁:

实例化后car中属性为Benz , Car.prototype中属性为Mazda

二. create

Object.create(对象,null) 构建对象和 new Obj () 效果一样
但是Object.create()提供了自定义原型的功能:

自定义原型:将test作为原型传进来:此时构造器还是指向Object (因为test是由Object创建出来的)

与obj1相反,此时obj2的构造函数指向obj

2 create

创建obj1空对象时 打印出什么属性都没有的空对象

增加属性obj1 = num
将obj1作为obj2的原型传进来,打印obj2:

从上所述:Object.create()有两个优点:

  1. 因为不是系统自带的,有些情况需要自定义prototype 时用Object.create()
  2. 把另外的对象作为 我这个对象的原型存在,从而达到继承,这样的形式更方便于查找

object.prototype 在顶端,是不是所有的对象都继承于它呢?
不是所有的对象都继承于Object.protyp! 由Object.create()创建的对象就不是

3. __prototype__只能是内置的,不能创造

自创的__proto__没有原型链的继承功能

4. toString()

undefined、null不能使用toString()方法:

console.log(undefined.toString());/报错
console.log(null.toString());/报错

var num = 1;
console.log(num.toString());/输出1

原因:原始值是没有属性的,硬要加属性就变成了包装类

数字对象中 toString()是内置的方法
undefined、null不能经过包装类转为对象,同时它们还也没有原型,不能去继承object、prototype

其实包装类就好比于 Object.create(Object.prototype)

5.原型方法的重写

系统内置的函数都有toString()方法:

对象类型的Number构造函数、对象类型的String构造函数······ 原型方法的重写: Object、Number两个类型的toString () 方法打印出来的结果不一样。

它们各自的prototype里面都有自己的toString()方法,并不是继承自Object.prototype:

如果Number里边没有toString()方法就会继承Object(因为Object是顶端的), 但是如果继承Object里面的话实现的功能就不对,不对的话就要对里面的方法进行重写。

所以实例化以后,他们的原型里面都保存了自己的toString():因为顶端的Object实现不了它想要的功能,所以系统在原型里面重新给它写了一个toString()

即它们各自的prototype里面都内置toString()方法,并不是继承自Object.prototype:

三. call和apply

call、apply能改变this指向

function test(){
console.log('a');
}
test(); /隐式的做了test.call();

apply:和call差不多,不过apply用的是数组
call/apply(函数名,实参)

apply能借用 别人写的方法: 直接apply(this),因为后面 compute .plus()等调用它时,this指向plus

可以调用另一个函数里面的属性跟方法:

<script>
   / 把原先this改成目前里面的this,并且把this里面保存的属性和方法拿过来了, 目的
   就是为了借用Teacher里面的属性和方法
   
        function Teacher(name, age) {
            this.name = name;
            this.age = age;
        }
        function Student(name, age, hobby) {
            Teacher.apply(this, [name, age]);
            this.hobby = hobby;
        }
        var s = new Student('张三', '18', '电脑');
        console.log(s);
        / 原先Teacher的方法并没有消失,还能继续使用
        var t = new Teacher('ad', '17');
        console.log(t);
        /所以apply、call只是在自己的函数里面调用另一个函数的方法、属性
    </script>

实战

两个构造函数 :
function Car(){}
function Person(){}
利用call、apply 将这两个合并在一起
可设置车的品牌、颜色、排量,再写一个构造消费者的函数,设置用户的名字、年龄、收入,通过选择的方法实例化该用户喜欢的车,再设置车的属性。