原型和原型链
题目:
1.如何正确判断一个变量是数组
2. 写一个原型链继承的例子
3.描述 new 一个对象的过程
知识点:
构造函数
构造函数-扩展
原型规则
原型链
instanceof
创建对象的几种方法:
1. 字面量方法
var o1 = {name:'o1'};
2. new Object():
var o2 = new Object({name:'o2'})
3. 构造函数:
var M = function(name){this.name = name};
var o3 = new M('o3');
4. Object.create():
var f = {name: 'o4'};
var o4 = Object.create(f);
构造函数
1 2 3 4 5 | function Foo(name,age){ this .name = name; this .age = age; this . class = 'class-1' ; } |
构造函数的扩展
var a = {};其实就是 var a = new Object()的语法糖
var a = []; 其实就是var a = new Array()语法糖
function Foo(){...};其实就是 var Foo = new Function(){...}
原型规则
1. 所有的引用类型(数组、对象、函数),都具有对象特性,可以自由扩展属性
1 2 3 | var obj = {};obj.a = 100; var arr =[];arr.a = 100; function fn(){}; fn.a = 100; |
2. 所有的引用类型(数组、对象、函数),都有一个__proto__隐式原型属性
1 2 3 4 5 6 7 | var obj = {};obj.a = 100; var arr =[];arr.a = 100; function fn(){}; fn.a = 100; console.log(obj.__proto__); console.log(arr.__proto__); console.log(fn.__proto__); |
3. 所有函数,都具有一个prototype显示原型属性
1 2 | function fn(){}; fn.a = 100; console.log(fn.prototype); |
4. 所有引用类型(数组、对象、函数)的__proto__属性值 指向它的构造函数的 prototype属性值
1 2 3 4 5 6 7 | var obj = {}; var arr =[]; function fn(){}; console.log(obj.__proto__ === Object.prototype) //true console.log(arr.__proto__ === Array.prototype) //true console.log(fn.__proto__ === Function.prototype) //true |
5. 当获取一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的__proto__(即构造函数的prototype)中寻找
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | function Foo(name){ this .name = name ; } Foo.prototype.alerName = function (){ alert( this .name); } var f = new Foo( 'zhangsan' ); f.printName = function (){ console.log( this .name); } f.printName(); f.alertName(); //f本身没有alertName,但Foo.prototype存在。所以可以找到并执行 |
原型链
简单理解就是:原型对象组成的链,一直通过隐示原型__proto__属性向上找,直到找到Object的原型时结束:
1 2 3 4 5 6 7 8 9 10 11 12 13 | function Foo(name,age){ this .name = name; } Foo.prototype.alertName = function (){ alert( this .name); } var f = new Foo( 'zhangsan' ); f.printName = function (){ console.log( this .name); } f.printName(); f.alertName(); f.toString(); //要去f.__proto__.__proto__中查找 |
以上面代码为例
1. f没有alertName方法,所以会去f.__proto__上去查找,因为f.__proto__ === Foo.prototype ,而Foo.prototype上有该方法。所以f.alertName()可以找到并被执行
2. f没有toString方法,所以会先去f.__proto__上去查找,Foo.prototype上也没有改方法;所以会继续去f.__proto__.__proto__上去查找(f.__proto__.__proto__ === Foo.prototype.__proto__ === Object.prototype),即Object.prototype上查找,所以f.toString()可以找到并执行
3.Object.prototype也同样有__proto__属性,只不过有点特殊,为null
我们把__proto__串联起来,直到Object.prototype.__proto__为止的链叫做原型链
下面用一张图来表示:
原型、实例、构造函数的关系?
通过代码来解释:
var M = function (name) { this.name = name; };
var o1 = new M('test');
console.log(M.prototype === o1.__proto__);//true
console.log(M.prototype.constructor === M);//true
1.实例是通过new 一个构造函数生成的,即代码中 o1就是实例,M就是构造函数。
2.构造函数的prototype属性指向的是原型(或者说原型对象)。即代码中 M.prototype === o1.__proto__
3. 实例的__proto__属性指向的也是原型(或者说原型对象)。即 代码中 M.prototype === o1.__proto__
4. 原型(或者说原型对象)对象的constructor指向的是构造函数。即代码中 M.prototype.constructor === M
原型链继承的例子
题目二解答:写一个原型链继承的例子?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | //封装DOM查询 类似jquery用法 function Elem(id){ this .elem = document.getElementById(id); } //jquery中 html 方法 Elem.prototype.html = function (val){ var elem = this .elem; if (val){ //设置html结构 elem.innerHTML = val; return this ; //链式操作 } else { //获取html结构 return elem.innerHTML; } } //事件 Elem.prototype.on = function (type,fn){ var elem = this .elem; elem.addEventListener(type,fn, false ); return this ; //链式调用 } |
调用:
1 2 3 4 5 6 7 8 9 | var div1 = new Elem( 'div1' ) div1.on( 'click' , function (){ alert( 'clicked' ); }) div1.html( '点击' ); console.log(div1.html()); |
instanceof 和 constructor
检测某个对象是不是另一个对象的实例可以用constructor 或者instanceof。
1. instanceof的原理是:判断实例对象的__proto__和生成该实例的构造函数的prototype是不是引用的同一个地址(或者说是不是相等),如果是返回ture;如果是原型链上向上找的构造函数同样返回true。
通过代码来解释:
var M = function (name) { this.name = name; }; var o1 = new M('test'); console.log(o1 instanceof M) //true console.log(o1 instanceof Object) //true
因为o1.__proto__ === M.prototype 所以 o1 instanceof M 返回为true;
因为 M.prototype.__proto__ === Object.prototype ,即Object是原型链上的构造函数,所以返回为true.
2. constructor是:判断实例是由哪个构造函数生成的。
通过代码来解释:
var M = function (name) { this.name = name; }; var o1 = new M('test'); console.log(o1.__proto__.constructor === M) //true console.log(o1.__proto__.constructor === Object) //false
题目一解答:如何准确判断一个变量是数组类型?
用instanceof来判断一个变量是否是数组,代码示例:
1 2 3 | var arr =[]; arr instanceof Array //true; typeof arr //object |
new 运算符的原理
当我们new一个实例对象时,JS执行时在内部做了很多工作,下面用伪代码模拟其工作流程:
new Foo('name') => { var obj = {}; //创建一个空对象
obj.__proto__ = Foo.prototype; //把obj的__proto__指向构造函数的原型对象(Foo.prototype),此时建立了obj的原型链 obj->Foo.prototype -> Object.prototype
var result = Foo.call(obj,'name');//改变this指向
return typeof result === 'object'? result : obj //判断result类型是不是对象,是返回result,不是返回obj
}
1)创建一个空对象obj,该对象obj继承构造函数的原型(Foo.prototype)
2) 将这个空对象的__proto__指向构造函数Foo的原型对象
3)构造函数Foo被执行,在执行的时候,相应的参数会被传入,同时上下文(this)会被替换成obj
4) 考察第三步的返回值result,如果无返回值或者返回一个非对象的值,则将obj作为新对象返回;否则将result作为新对象返回
题目三解答:描述 new 一个对象的过程?
1. 首先创建一个新对象{};
2. 将构函数的作用域赋给新的对象向(因此 this指向了这个新创建的对象);
3. 执行构造函数中的代码(为这个新对象添加属性);
4. 返回这个新对象
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!