06-原型和原型链

一、公有属性和私有属性

1、一切都是对象:
对象是属性的无序集合 操作集合CDUR create delete updata read
每一个

2、函数多种角色:
1)普通函数
2)对象的方法
3)普通对象
4)类(构造函数,构造器)

3、只要是一个对象,就一定会有__proto__属性浏览器中也显示为 [[prototype]] 叫:隐式原型 ,隐式原型的值是一个对象

<script>
    // obj是一个对象  属性的无序集合
    // 属性:
    // 1)私有属性
    // 2)共有属性:沿着__proto__找到的属性都是公有属性
    let obj = {
        name: 'wc' //私有属性
    }
    console.log(obj);
</script>
<script>
    let obj = {
        name: 'wc',
        age: 18
    }
    console.log(obj); //{name: 'wc', age: 18}


    // obj打点查阅hasOwnProperty,自己私有属性没有,就沿着__proto__去共有属性找
    // 只有找到了这个属性何当大,才能使用这个属性或方法
    console.log(obj.hasOwnProperty); //ƒ hasOwnProperty() { [native code] }
    console.log(obj.wc); //undefined
    //hasOwnProperty()判断一个属性是否是私有属性
    console.log(obj.hasOwnProperty('name')); //true
    console.log(obj.hasOwnProperty('age')); //true
    console.log(obj.hasOwnProperty('address')); //false
    // 判断hasOwnProperty是否是obj的私有属性
    console.log(obj.hasOwnProperty('hasOwnProperty')); //false

   

    // 引出原型链
    
</script>

4、 原型链比较 作用域链:
1) a.b 找a 去执行上下文EC里面找,如果找不到就去父的EC中找,如果在找不到就去父的父EC中找,直到找到ECA,如果找到不到就报错,a is not defined

2)a.b 找b先找不到就沿着__proto__去公有属性中找,如果沿着__proto__连起来的原型链找不到,返回undefined,因为查找一个对象上不存在的属性

<script>
    let obj = {
            name: 'wc',
            age: 18,
        }
        // __proto__  对应的值是一个对象,这个对象叫:原型对象
    console.log(obj.__proto__);
    // 原型对象,也是对象,只要是个对象,身上就有一个__proto__
    console.log(obj.__proto__.__proto__); //null
    //若一直找下去,就找到null


    // obj.__proto__.hasOwnProperty('name')  此处obj.__proto__对应的是原型对象,hasOwnProperty相对于原型对象来说是私有属性
</script>

5、in是判断一个属性是否属于某个对象

<script>
    let arr = [];
    // in  判断是否有这个属性,不论是公有还是私有
    // hasOwnProperty   判断是否是私有属性

    // in是用来判断一个属性是否属于某个对象
    // 不管是私有还是公有
    console.log('push' in arr); //trues
</script>

二、每个构造器都有个prototype属性

  • 每个 对象 身上都有一个__proto__属性隐式原型
  • 每个 构造器(类) 身上都有一个prototype属性显式原型
  • 实例对象的隐式原型构造器的显示原型指向同一个对象:原型对象
<script>
    // arr1是对象  有隐式原型
    // Array是类,也叫构造器,本质是函数
    // 函数身上都有一个显示原型
    let arr1 = new Array('wc', 'xq');
    let arr2 = new Array('jj');
    // 构造器的现实原型与实例对象的隐式原型指向同一个对象,所以二者相等
    console.log(Array.prototype == arr1.__proto__); //true
    console.log(Array.prototype == arr2.__proto__); //true
    // 沿着原型链找原型对象
    console.log(arr1.__proto__.__proto__ == Object.prototype); //true
    console.log(arr1.__proto__.__proto__.__proto__); //null

    let obj1 = new Object();
</script>
  • onstructor 构造器 原型对象上有个constructor属于性指向构造函数(构造器)
<script>
    // 构造器    构造器,每个构造器上都有一个显示原型
    function Person(name, age) {
        this.name = name;
        this.age = age;
    }
    // 每一个 对象上都有一个隐式原型__proto__
    let p = new Person('wc', 18);
    //onstructor 构造器   原型对象上有个constructor属于性指向构造函数
    console.log(p.__proto__.constructor == Person); //trues
</script>

总结:
1)一起切都是对象
2)对象是属性的无序集合
3)属性分公有属性和私有属性
4)每个对象身上都有一个__proto__属性,叫 隐式原型
5)每一个构造函数(类)身上都有一个prototype属性,叫显示原型
6)每一个实例对象的隐式原型__proto__和函数的显示原型prototype都指向一个对象,叫原型对象
7)每一个原型对象身上有一个constructor属性,指向构造函数(类)本身。

三、原型链练习

  • 调用一个函数,调用处返回一个值,函数内部没有return,或return后面为空的话返回的是undefined
<script>
    function Person() {

    }

    // 没有new 叫函数调用
    // Person()就是一个值   返回值
    // 返回值为und
    // 
    let p = Person();
    console.log(p); //undefined
</script>

  • 理解new运算符返回对象的过程
<script>
    // Person是个函数
    // 函数的角色:
    // 1) 普通函数
    // 2) 对象
    // 3) 方法
    // 4) 类(构造器)



    // 理解new运算符返回对象的过程:
    function Person() {
        // 1)在Person函数中创建一个空对象  {}  0x666  ==>
        // 2)this绑定  this = 0x666;this 指向上面的对象
        this.a = 1;
        this.b = 2;
        this.c = 3;
        // 3)执行Peron函数中的代码
        // 4)返回对象  返回0x666


    }
    // 把Person 当成一个对象 给这个对象上添加xxx属性   值是ok,此处应用构造函数内部的this相区别
    Person.xxx = 'ok';

    // new 也是运算符  这个运算符结果是对象
    // 只要你敢new 出来的100%就是对象
   let p = new Person();
</script>
  • Fn即可是函数也可是构造器(类)还可是对象
<script>
    function Fn() {
        this.x = 10;
        this.y = 20;
    }
    Fn.n = 1000;
    Fn.say = function() {
        console.log('hello...');
    }

    // 在Fn的原型对象身上增加sum属方法
    Fn.prototype.sum = function() {
        return this.x + this.y;

    }

    // new一个对象
    let f1 = new Fn();
    f1.sum();
    console.log(f1.sum()); //30
    // Fn.__proto__指向Function的原型对象(此处Fn指的是对象),Fn.prototype指Fn的原型对象(此处Fn指的是构造器)
    console.log(Fn.prototype == Fn.__proto__); //false
    Fn.sum(); //Uncaught TypeError: Fn.sum is not a function
</script>

Fn.__proto__指向Function的原型对象(此处Fn指的是对象),Fn.prototype指Fn的原型对象(此处Fn指的是构造器)
在这里插入图片描述

<script>
    function C1(name) { //参数没有传参,默认情况下是undefined
        if (name) {
            this.name = name;
        }
    }

    function C2(name) {
        this.name = name;
    }

    function C3(name) {
        thi.name = name || 'hello'; //undefined 不能决定整个所以取'hello'
    }
    C1.prototype.name = 'Tom';
    C2.prototype.name = 'Tom';
    C3.prototype.name = 'Tom';
    console.log((new C1().name) + (new C2().name) + (new C3().name)); // 'Tom'+undefoined+'hello'
</script>

函数形参未传值默认是undefined

<script>
    function C1(name) { //参数没有传值,默认情况下是undefined
        if (name) {
            this.name = name;
        }
    }

    function C2(name) {
        this.name = name;
    }

    function C3(name) {
        thi.name = name || 'hello'; //undefined 不能决定整个所以取'hello'
    }
    C1.prototype.name = 'Tom';
    C2.prototype.name = 'Tom';
    C3.prototype.name = 'Tom';
    // 实例的对象上都有name属性
    console.log((new C1().name) + (new C2().name) + (new C3().name)); // 'Tom'+undefoined+'hello'
</script>

手写new的实现过程

<script>
    // 手写new的实现
    //new是一个运算符
    // 只能用函数去模拟new 运算符

    function myNew(Fn, ...args) {
    	//创建一个空对象
        let obj = {};
        //令新创建对象的隐式原型,与构造器的显示原型相等
        obj.__proto__ = Fn.prototype;
        //绑定Fn中的this指向新创建的对象obj;
        Fn.apply(obj, args);
        return obj;
    };

    function person(a, b, c) {
        this.a = a;
        this.a = b;
        this.a = c;
    }
    person.prototype.coding = function() {
        console.log('coding...');
    }

    let p = myNew(person, 1, 2, 3);
    console.log(p.a);
</script>

阶段性复习(面试题)

01-EC和作用域链

1.什么是代码段:
答:一个script 标签就是一个代码段,一个网页中可以有n个代码段,代码段一个个从上到下执行。上面代码段定义的数据(变量、函数),在小面的代码段中可以使用。代码段中定义的数据在上面代码段中不能使用。如果代码段报错,不影响下面的代码段执行,它们质检室彼此独立的。

2.JS代码在执行时各阶段:
1)预解析 预编译
a)var 声明的变量提升到最前面
b)全局代码中的函数整体提升到代码段的最前面
c)函数内的var 声明的局部变量也会提升到函数体最前面
2)代码段从上到下执行

3 数据存储:
基本数据类型和引用类型的地址存在栈区,引用数据存储在堆区,

4.执行上下文:
1)全局代码、局部代码
当全局代码执行,就会产生全局执行上下文,当函数调用,就产生局部执行上下文,执行上下文要入栈,栈也是一种数据结构,是一种先进后出的数据结构。
2)ECS:执行上下文栈
3)ECG:全局代码执行产生全局执行上下文ECG,进入ECS,ECG中变量对象VO也是GO,VO或GO包含:全局变量,函数的地址,地址指向堆区对应的引用地址。每调用一个函数就产生一个EC(局部执行上下文),EC中包含三个部分,第一部分:VO或AO内部包含:形参arguments、局部变量,内部定义的函数,第二部分:作用域链(自己的VO和父级的VO),第三部分:this绑定。
4)执行上下文EC作用:
在代码执行过程中为代码提供数据
VO:Variable Object
AO:Active Object

5.什么是作用域链:
答:作用域链就是数据在EC中的查找规则(机制)

02-深入变量和闭包

1)加Var 的变量的和不加var的变量有什么区别?
a)加var的变量会提升声明,不加var不提升, 对开发而言是不好的
b)加var的变量可以是全局变量,也可以是局部变量,不加var的变量,只能是全局变量
c)不管加不加var,只要是全局变量都会挂到GO上。
d)建议,不要使用加var的变量,更不要使用不加var的变量,清一色使用let

2)使用let声明的变量的特点:
a)粗略理解为不会提升,详细点可以理解为提升了但是没有初始化(赋值),就是不会是undefined,变量没有初始化是不能被访问的。
b)let和{}配合形成块级作用域(重要),利用这个块级作用域可以解决很多问题
c)不能重复声明,不会挂在GO(window)上

3)使用const声明常量的特点:
a)const声明的常量不能修改。如果使用赋值方法修改常量会报错。
b)const声明的常量在声明时,必须要赋值。一个const对应一个数据。
c)其他的特点和let一样。

4) 高阶函数:
定义:如果一个函数的参数是函数,或一个函数的返回值是函数。就可以称之为高阶函数。
JS中有很多内置的高阶函数,特别是数组中的。

5)闭包:
从形式上看:要有函数嵌套,内层函数使用外层函数中的变量,让外层函数中的变量活的更久一些。
也可理解为:一个不能被立即释放的栈空间就是闭包。闭包可以让我们使用全局变量那样使用局部变量。
闭包作用:
保护:由于闭包中的变量存在内部,外界不能被直接访问。
保存:延长变量的生命周期。可以让我们使用全局变量那样使用局部变量。

03-this绑定 及优先级(重点)

this:
在产生执行上下文绑定值,没有产生EC,是不能确定this是什么。
不能给this手动赋值,只有在调用函数时, 才能明确this指的是什么。

this绑定规则:
this永远指向最后调用它的对象
1)默认绑定
独立函数调用 this指向调用者
2)隐式绑定
通过打点调用 方法中this就看谁在点前面
3)显示绑定
call apply bind 函数对象的角色
fn.call() fn.apply() fn.bind()
call: 1)让fn执行 2)让fn指向this指向()中的第1个参数
apply: 1)让fn执行 2)让fn指向this指向()中的第1个参数 3)参数是数组
bind:1)不会让this执行 2)返回一个绑定this后的新函数
4)new绑定

04、05、06-OOP面向对象编程

一切都是对象
对象是属性的无序集合,操作集合,CURD
创建对象的N种方式:
a)通过字面创建对象
b)工厂函数创建对象 创建的对象都是Object的实例 let obj = new Object()。
c)构造器 new构造器 缺点:内存空间浪费

08-原型和原型链

原型和原型链:
let obj = {a:1, b:2 ,c:3}
每一个对象身上都有一个__proto__属性,对应一个对象,这个对象叫做原型对象

三角关系:
1)每一个构造器身上都有一个prototype属性 叫做显示原型
2)每一个对象上都有一个__proto__属性,叫隐式原型
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值