js面试题

  1. 介绍JS的基本数据类型
    Undefined、Null、Boolean、Number、String 可以用typeof操作符来检测
    null和undefined的区别:null表示没有对象,即此处不应该有值 undefined表示缺少值,此处应该有一个值,但是还没有定义。

  2. 介绍JS有哪些内置对象? Object是JavaScript中所有对象的父对象。
    数据封装类对象:Object、Array、Boolean、Number和String
    其他对象:Function、Arguments、Math、Date、RegExp、Error

  3. 什么是JS中的事件委托? 事件委托是解决“事件处理程序过多”的方案。事件委托利用了事件冒泡,只指定一个事件处理,就可以管理某一类型的所有事件。
    也就是说:利用事件冒泡的原理,把相关事件绑定到父级元素上,触发执行效果。
    例如:click事件会一直冒泡到document层。也就是说,我们可以为整个页面指定一个onclick事件处理程序,而不必给每个可单击的元素分别添加事件处理程序。

  4. 什么是Ajax,其核心是什么? Ajax全称为Asynchronous JavaScript And XML
    向服务器发送异步请求的时候,我们不必等待结果返回,而是同时做其他的事情,等到结果返回后,程序会根据设定(callback)进行后续操作,此时,页面也不会发生刷新,提高用户体验。
    Ajax的核心对象就是xhr,XMLHttpRequest
    发生Ajax请求的过程可分为以下六步:
    创建XMLHttpRequest对象,也就是创建一个异步调用对象。
    创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息。
    设置响应HTTP请求状态变化的函数
    发送HTTP请求
    获取异步调用返回的数据
    使用JS操作DOM或数据驱动框架来实现局部刷新
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function(){
    if(xhr.readyState === 4){
    if((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304)
    var data = JSON.parse(xhr.responseText);
    callback(data);
    }
    };
    xhr.open(‘get’,url,true);
    xhr.send(null);
    5.JS中如何实现继承?

  5. 什么是闭包?
    闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。
    闭包的特性:
    1). 函数内再嵌套函数
    2). 内部函数可以引用外层的参数和变量
    3). 参数和变量不会被垃圾回收机制回收
    例子:
    function createFunction(){
    var result = new Array();
    for(var i=0; i<10; i++){
    result[i] = (function(num){
    return function(){
    return num;
    };
    })(i);
    }
    return result;
    }
    上面这个例子中有两个闭包,外层的那个立即执行的匿名函数是一个闭包。在调用每个匿名函数时,我们传入了变量i,由于函数参数是按值传递,所以就会将变量i的当前值赋值给参数num。而在每个匿名函数内部,我们创建并返回了一个可以访问num的闭包。这样一来,result数组中的每个函数都有了自己num变量的一个副本,因此就可以返回各自不同的数值了。

  6. new操作符具体干了什么?
    创建一个空对象,并且this变量引用该对象,同时还继承了该函数的原型。
    属性和方法被加入到this引用的对象中。
    新创建的对象由this所引用,并且最后隐式返回this。
    8.JSON的了解? JSON是一种轻量级的数据交换格式。 它是基于JavaScript的一个子集。数据格式简单,易于读写,占用带宽小。
    9.Json对象克隆:let newJson = JSON.parse(JSON.stringify(json));

  7. eval是做什么的? eval函数的功能是把对应的字符串解析成JS代码并运行。
    应该避免使用eval,不安全,且非常消耗性能(2次处理,一次解析成JS语句,一次执行)

  8. 如何解决跨域问题? JSONP、window.name、window.postMessage、iframe

  9. 模块化开发怎么做? 立即执行函数,不暴露私有成员。

  10. 函数中的arguments是什么,是数组吗?若不是,如何将它转化为真正的数组? arguments是一个类数组对象(它并不是Array的实例),它可以通过方括号语法访问每个元素,也可以使用length属性来确定调用函数时传递进来了多少个参数。 Array.prototype.slice.call(arguments)

  11. 列举JavaScript中的typeof函数的可能结果,如何区分:{}和[]类型?
    typeof的值可能有:”number”,”string”,”boolean”,”object”,”undefined”,”function”
    区分{}和[]:

  12. [].constructor === Array,t={},t.constructor === Object

  13. [] instanceof Array ==> true
    t = {},t instanceof Object ==> true

  14. Function中的call、apply、bind的区别是什么?请分别写出示例代码
    bind()方法会创建一个新函数,当这个新函数被调用时,它的this值是传递给bind()的第一个参数,它的参数是bind()的其他参数和其原本参数。
    bind()最简单的用法是创建一个函数,使这个函数不论怎么调用都有同样的this值。JavaScript新手经常犯的一个错误就是将一个方法(假设方法中用到了this对象)从对象中拿出来,然后再调用,并且希望方法中的this是原来的对象。如果 不做特殊处理的话,一般会丢失原来的对象。用原来的函数和原来的对象创建一个绑定函数,可以漂亮地解决这个问题:
    this.x = 9;
    var module = {
    x: 81,
    getX: function(){ return this.x }
    }
    module.getX(); //81
    var retrieveX = module.getX;
    //注意这段代码要在非严格模式下运行,严格模式下会报错
    //因为严格模式下this为undefined
    retrieveX(); //9,因为此时方法中的this指向的是全局对象
    //用bind方法解决这个问题
    var boundGetX = retrieve.bind(module);
    boundGetX(); //81
    apply()和call()很类似,都是在指定指定this值和参数的情况下调用某个函数。差别只是apply()方法的参数是以数组或类数组对象的形式存在。call()方法的参数是一个参数列表(逗号隔开)。
    实例:比如之前讲到的把函数的arguments类数组对象转成一个数组的方法:Array.prototype.slice.call(arguments)
    使用jQuery,找到id为“selector”的select标签中拥有’data-target’属性,且值为“isme”的option的值? $(‘select#selector>option[data-target=”isme”]’).val()

  15. 设计一个算法,合并两个有序数组为一个有序数组?
    与归并排序类似
    var a = [3,6,8,9,15];
    var b = [1,2,5,7,12,16];
    var result = [];
    function mergeArray(a,b,result){
    var i=0,j=0,index=0;
    while(i<a.length && j<b.length){
    if(a[i] <= b[j]){
    result[index++] = a[i++];
    } else{
    result[index++] = b[j++];
    }
    }
    while(i<a.length){
    result[index++] = a[i++];
    }
    while(j<b.length){
    result[index++] = b[j++];
    }
    }
    16.JavaScript中的作用域链?
    全局函数无法查看局部函数的内部细节,但局部函数可以查看其上层的函数细节,直至全局细节。
    当需要从局部函数查找某一属性或方法时,如果当前作用域没有找到该属性或方法,就会上溯到上层作用域查找,直至全局函数,这种组织形式就是作用域链。
    如下一段代码:
    var a = []; a[100] = 1; console.log(a.length); console.log(a[0]); a[200] =
    undefined; console.log(a.length); console.lolg(a[‘100’]);
    请问四条log语句分别输出什么?
    【答案】分别输出 101,undefined, 201,1
    parseInt(‘1.9’); parseInt(‘hello’)分别返回什么值?
    【答案】分别返回 1, NaN
    =的区别
    【参考答案】==在比较的时候可以自动转换数据类型。===严格比较,不会进行自动转换,要求进行比较的操作数必须类型一致,不一致时返回flase。

函数中this指向什么? call, apply, bind的用法和区别。
【参考答案】this指代函数的运行环境,执行obj.func()时,this指向obj,直接执行func()时,this指向全局环境。
call, apply, bind都可以改变函数执行时的运行环境,即this的指向。

call和apply都是调用时立刻执行的,而bind调用后返回了绑定this对象的原函数,bind比较适合将this绑定后的函数传入到其他函数中去执行,特别是作为回掉函数异步执行。
call和bind的参数第一个参数是要绑定的对象,后面是要传入原函数的多个参数;而apply第二个参数必须是一个数组,数组中是要传入的参数。

说说对 prototype和 __proto __ 的理解
【参考答案】prototype是函数才有的属性,prototype本身也是个函数对象;__proto __是所有对象都有的属性,__proto
__指向构造它的对象的对象的prototype。例如:

var o = new Object() o.proto == Object.prototype < true
o是Object构造出的对象,o的__proto
__指向Object的prototype,这样o可以使用Object.prototype里面的方法。原型链:当js查找对象的属性时,先查找对象自身是否具有该属性,如果没有,就会去__proto
__指向的prototype对象上查找,直到找到或者__proto __为null

  • 使用构造函数实现一个类Foo,需要有属性 count, 方法bar(), 并且写出创建该类对象的方法
    【参考答案】 function Foo(){ this.count = 0; } Foo.prototype.bar = function(){ }
    var foo = new Foo(); foo.bar();
  • 以下代码片段输出是什么,为什么?如果想输出0,1,2请问如何修改?
    var s = [];
    function foo() {
    for(var i=0; i<3; i++){
    s[i] = function(){
    console.log(i);
    }
    }
    }
    foo();
    s0;
    s1;
    s2;

【参考答案】输出3,3,3。因为foo()函数执行时生成了三个闭包,这三个闭包绑定了同一个变量i,第三个闭包生成时,i的值为3。因此执行这三个闭包时都会输出3。
修改为输出0,1,2的原则是让三个闭包绑定不同的变量,所以在生成闭包时就要区分出来,一种修改方法如下:
var s = [];
function foo() {
for(var i=0; i<3; i++){
s[i] = function(index){
return function(){ document.write(index);
}
}(i);
}
}
foo();
s0;
s1;
s2;

  • 说说ES6为什么要引入let关键字
    【参考答案】因为要解决var声明对象产生的问题。
    var是函数级作用域,而let是块作用域。
    var存在变量提升,即变量可以在声明之前使用,值为undefined,而let声明的变量如果在声明之前使用会抛出一个错误。
    另外let不允许重复声明变量。
    ES6 规定暂时let、const语句不出现变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。

ES6都有啥变动?#
1.变量
2.函数
3.数组
4.字符串
5.面向对象
6.Promise, 用来做串行话的异步请求
7.generator,把同步操作拆成异步操作
8.模块化
变量#
var#
1.可以重复声明
2.无法限制修改
3.没有块级作用域
let#
1.不能重复声明
2.变量,可以修改
3.块级作用域
const#
1.不能重复声明
2.常量,不可以修改
3.块级作用域
函数#
箭头函数#
()=>{
} lambda表达式,是模仿Python里边的。
1.如果只有一个参数,()可以省略。
2.如果只有一个return,{}可以省略
参数#
1.参数扩展/展开
数组扩展:function show(a,b,…args){}
​ rest Parameter必须是最后一个
展开数组: let arr = [1,2,3] show(…arr),
2.默认参数
​ function show (a, b=4,c=12){} show(99,12)
解构赋值#
1.左右两边结构必须一样
2.右边必须是个东西
3.声明和赋值不能分开(必须在一句话里完成)
例如:
数组: let [a,b,c] = [1,2,3] console.log(a,b,c)
json: let {a,b,c} = {a:5,b:1,c:3}
混合: let[{a,b},[n1,n2],num,str] = [{a:12,b:3},[1,2],3,f]
数组#
Map 映射 一个对一个
​ let arr = [12,3,4] ; arr.map()
reduce 汇总
· 算总和: arr.reduce(tmp,item,index)
​ 算平均数:
filter 过滤器
​ arr.filter(item=>return true/false)
foreach 循环
​ arr.forEach(item,index => ())

字符串#
1.
多了两个新方法
2.
startWith str.startWith(“http://”) ,
3.
endWith str.endWith(".png")
4.
5.
字符串模板
6.
let str = a${b}c; 反单引号
7.
i.直接把东西塞到字符串里边。 ${东西}
8.
ii.可以折行
9.

ES6的面向对象#
1.class 关键字,构造器和类分开了
2.class里边直接加方法
class User{
​ constructor(name,pass){
​ this.name = name;
​ this.pass = pass ;
​ }
​ showName(){
​ alert(name);
​ }
}
继承

面向对象应用–React
React: 基于组件开发
1.组件化-class
2.JSX==babel == brower.js
JSON#
1.
JSON对象
2.
1.JSON.stringify(json): 串行化,将json对象转换成字符串
2.JSON.parse(str) : 解析
3.
JSON的简写
4.
1.名字和值(key和value)一样,留一个就行
2.方法 :function一块删
例如:
show : function(){…}
show(){…}
JSON对象:串行化,解析。
JSON的标准写法:
1.只能用双引号
2.所有的名字都必须用引号抱起来
Promise#
Promise 许诺,承诺。
异步:操作和操作之间是没关系的,同时进行多个操作
同步:操作之间相关,同时只能做一件事
异步:代码更复杂
同步:代码简单
Promise,用同步的方式,来写异步代码
1.
Promise.all
2.
3.
Promise.racs
4.

jQuery自带Promise。
AJax的返回值就是Promise。

Generator-生成器函数#
普通函数-一路到底 function show(){}
generator函数-中间能停 function *show(){ a , yield ; c } 返回对象.next() 继续往下走。

JavaScript 和 TypeScript 的概要介绍
JavaScript 是轻量级的解释性脚本语言,可嵌入到 HTML 页面中,在浏览器端执行。而TypeScript 是JavaScript 的超集,即包含JavaScript 的所有元素,能运行JavaScript 的代码,并扩展了JavaScript 的语法。相比于JavaScript ,它还增加了静态类型、类、模块、接口和类型注解方面的功能,更易于大项目的开发。
TypeScript 相比于JavaScript 的优势总结为以下几点:
1.便于开发人员做注释。
2.能帮助开发人员检测出错误并修改。
3.TypeScript工具使重构更变的容易、快捷。
4.TypeScript 引入了 JavaScript 中没有的“类”概念。
5.TypeScript 中引入了模块的概念,可以把声明、数据、函数和类封装在模块中。
6.。类型安全功能能在编码期间检测错误,这为开发人员创建了一个更高效的编码和调试过程。
如果要用一句话解释TS里的namespace与JS里module的区别
TS里的namespace是跨文件的,JS里的module是以文件为单位的,一个文件一个module。
TS里的namespace主要是解决命名冲突的问题,会在全局生成一个对象,定义在namespace内部的类都要通过这个对象的属性访问,例如 egret.DisplayObject,egret就是namespace的对象,DisplayObject则是那个类名。因为是注册到全局的,所以跨文件也能正常使用,不同的文件能够读取其他文件注册在全局的命名空间内的信息,也可以注册自己的。namespace其实比较像其他面向对象编程语言里包名的概念。
而JS里的module,主要是解决加载依赖关系的。跟文件绑定在一起,一个文件就是一个module。在一个文件中访问另一个文件必须要加载另一个文件。在NodeJS里是用CommonJS处理模块加载,因为是运行在本地,所以可以同步加载,写起来也比较方便。用到一个文件就require它一下,作为一个变量。而在Web端的RequireJS使用的是AMD处理模块加载,是异步的。其实就是把所有代码写在回调里,先去异步加载依赖的所有文件。
所以可以简单的理解,namespace的概念等同于包名,module的概念等同于文件。

interface和type区别
类型别名 type
类型别名用来给一个类型起个新名字,多用于联合类型:type 声明可以定义联合类型,基本类型等多种类型,而 interface 只能定义对象类型
接口(Interfaces)是一个很重要的概念,可以理解为一种规范或者约束,用来描述 对象(object) 的形状 或者对 类(class) 的行为 进行抽象。
接口定义
使用 interface 定义接口, 接口名称一般首字母大写,定义接口的时候,只定义声明即可,不包含具体内容:
// 定义一个接口 Person
interface Person {
name: string;
age: number;}
// 定义一个个变量,它的类型是 Person
let tom: Person = {
name: ‘Tom’,
age: 25};
实现接口的时候,要实现里面的内容,定义的变量比接口少了或多了属性都是不允许的
let tom: Person = {
name: ‘tom’}// => 编译报错,少了age属性

可选属性
使用 ? 代表可选属性, 即该属性可以不存在, 但不允许添加未定义的属性
interface Person {
name: string;
age?: number;}
let tom: Person = {
name: ‘tom’}// age是可选属性

任意属性
定义了任意属性后可以添加未定义的属性,并可以指定属性值的类型
interface Person03 {
name: string;
age?: number;
[propName: string]: any;}
let tom04: Person03 = {
name: ‘Tom’,
age: 25,
gender: ‘male’};

定义了任意属性,那么确定属性和可选属性都必须是它的子属性
interface Person {
name: string;
age?: number;
[propName: string]: string;}// 编译报错:Person定义了一个任意属性,其值为string类型。则Person的所有属性都必须为string类型,而age为number类型

只读属性 readonly
interface Person {
readonly id: number;
name: string;
age?: number;
[propName: string]: any;}

只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候
let person: Person = {
id: 100,
name: ‘tom’,}
person05.id = 90;// => 编译报错:id为只读, 不可修改
let person2: Person = {
name: ‘welson’,
age: 2}// => 编译报错:给对象 person2 赋值,未定义只读属性id
person2.id = 1;// => 编译报错:id为只读, 不可修改

前言

JS作为面向对象的弱类型语言,继承也是其非常强大的特性之一。那么如何在JS中实现继承呢?让我们拭目以待。

JS继承的实现方式

既然要实现继承,那么首先我们得有一个父类,代码如下:


  
  
  1. // 定义一个动物类
  2. function Animal (name) {
  3. // 属性
  4. this.name = name || 'Animal';
  5. // 实例方法
  6. this.sleep = function(){
  7. console.log( this.name + '正在睡觉!');
  8. }
  9. }
  10. // 原型方法
  11. Animal.prototype.eat = function(food) {
  12. console.log( this.name + '正在吃:' + food);
  13. };

1、原型链继承

核心: 将父类的实例作为子类的原型


  
  
  1. function Cat(){
  2. }
  3. Cat.prototype = new Animal();
  4. Cat.prototype.name = 'cat';
  5. // Test Code
  6. var cat = new Cat();
  7. console.log(cat.name);
  8. console.log(cat.eat( 'fish'));
  9. console.log(cat.sleep());
  10. console.log(cat instanceof Animal); //true
  11. console.log(cat instanceof Cat); //true

特点:

  1. 非常纯粹的继承关系,实例是子类的实例,也是父类的实例
  2. 父类新增原型方法/原型属性,子类都能访问到
  3. 简单,易于实现

缺点:

  1. 要想为子类新增属性和方法,必须要在new Animal()这样的语句之后执行,不能放到构造器中
  2. 无法实现多继承
  3. 来自原型对象的所有属性被所有实例共享(来自原型对象的引用属性是所有实例共享的)(详细请看附录代码: 示例1)
  4. 创建子类实例时,无法向父类构造函数传参

推荐指数:★★(3、4两大致命缺陷)

2017-8-17 10:21:43补充:感谢 MMHS 指出。缺点1中描述有误:可以在Cat构造函数中,为Cat实例增加实例属性。如果要新增原型属性和方法,则必须放在new Animal()这样的语句之后执行。

2018-9-10 00:03:45补充:感谢 IRVING_J 指出。缺点3中的描述不够充分。更正为:来自原型对象的所有属性被所有实例共享。

2、构造继承

核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)


  
  
  1. function Cat(name){
  2. Animal.call( this);
  3. this.name = name || 'Tom';
  4. }
  5. // Test Code
  6. var cat = new Cat();
  7. console.log(cat.name);
  8. console.log(cat.sleep());
  9. console.log(cat instanceof Animal); // false
  10. console.log(cat instanceof Cat); // true

特点:

  1. 解决了1中,子类实例共享父类引用属性的问题
  2. 创建子类实例时,可以向父类传递参数
  3. 可以实现多继承(call多个父类对象)

缺点:

  1. 实例并不是父类的实例,只是子类的实例
  2. 只能继承父类的实例属性和方法,不能继承原型属性/方法
  3. 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能

推荐指数:★★(缺点3)

3、实例继承

核心:为父类实例添加新特性,作为子类实例返回


  
  
  1. function Cat(name){
  2. var instance = new Animal();
  3. instance.name = name || 'Tom';
  4. return instance;
  5. }
  6. // Test Code
  7. var cat = new Cat();
  8. console.log(cat.name);
  9. console.log(cat.sleep());
  10. console.log(cat instanceof Animal); // true
  11. console.log(cat instanceof Cat); // false

特点:

  1. 不限制调用方式,不管是new 子类()还是子类(),返回的对象具有相同的效果

缺点:

  1. 实例是父类的实例,不是子类的实例
  2. 不支持多继承

推荐指数:★★

4、拷贝继承


  
  
  1. function Cat(name){
  2. var animal = new Animal();
  3. for( var p in animal){
  4. Cat.prototype[p] = animal[p];
  5. }
  6. Cat.prototype.name = name || 'Tom';
  7. }
  8. // Test Code
  9. var cat = new Cat();
  10. console.log(cat.name);
  11. console.log(cat.sleep());
  12. console.log(cat instanceof Animal); // false
  13. console.log(cat instanceof Cat); // true

特点:

  1. 支持多继承

缺点:

  1. 效率较低,内存占用高(因为要拷贝父类的属性)
  2. 无法获取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到)

推荐指数:★(缺点1)

5、组合继承

核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用


  
  
  1. function Cat(name){
  2. Animal.call( this);
  3. this.name = name || 'Tom';
  4. }
  5. Cat.prototype = new Animal();

    // 感谢 @学无止境c 的提醒,组合继承也是需要修复构造函数指向的。

    Cat.prototype.constructor = Cat;
  6. // Test Code
  7. var cat = new Cat();
  8. console.log(cat.name);
  9. console.log(cat.sleep());
  10. console.log(cat instanceof Animal); // true
  11. console.log(cat instanceof Cat); // true

特点:

  1. 弥补了方式2的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法
  2. 既是子类的实例,也是父类的实例
  3. 不存在引用属性共享问题
  4. 可传参
  5. 函数可复用

缺点:

  1. 调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)

推荐指数:★★★★(仅仅多消耗了一点内存)

6、寄生组合继承

核心:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点


  
  
  1. function Cat(name){
  2. Animal.call( this);
  3. this.name = name || 'Tom';
  4. }
  5. ( function(){
  6. // 创建一个没有实例方法的类
  7. var Super = function(){};
  8. Super.prototype = Animal.prototype;
  9. //将实例作为子类的原型
  10. Cat.prototype = new Super();
  11. })();
  12. // Test Code
  13. var cat = new Cat();
  14. console.log(cat.name);
  15. console.log(cat.sleep());
  16. console.log(cat instanceof Animal); // true
  17. console.log(cat instanceof Cat); //true

    感谢 @bluedrink 提醒,该实现没有修复constructor。

    Cat.prototype.constructor = Cat; // 需要修复下构造函数

特点:

  1. 堪称完美

缺点:

  1. 实现较为复杂

推荐指数:★★★★(实现复杂,扣掉一颗星)

附录代码:

示例一:


  
  
  1. function Animal (name) {
  2. // 属性
  3. this.name = name || 'Animal';
  4. // 实例方法
  5. this.sleep = function(){
  6. console.log( this.name + '正在睡觉!');
  7. }
  8. //实例引用属性
  9. this.features = [];
  10. }
  11. function Cat(name){
  12. }
  13. Cat.prototype = new Animal();
  14. var tom = new Cat( 'Tom');
  15. var kissy = new Cat( 'Kissy');
  16. console.log(tom.name); // "Animal"
  17. console.log(kissy.name); // "Animal"
  18. console.log(tom.features); // []
  19. console.log(kissy.features); // []
  20. tom.name = 'Tom-New Name';
  21. tom.features.push( 'eat');
  22. //针对父类实例值类型成员的更改,不影响
  23. console.log(tom.name); // "Tom-New Name"
  24. console.log(kissy.name); // "Animal"
  25. //针对父类实例引用类型成员的更改,会通过影响其他子类实例
  26. console.log(tom.features); // ['eat']
  27. console.log(kissy.features); // ['eat']
  28. 原因分析:
  29. 关键点:属性查找过程
  30. 执行tom.features.push,首先找tom对象的实例属性(找不到),
  31. 那么去原型对象中找,也就是Animal的实例。发现有,那么就直接在这个对象的
  32. features属性中插入值。
  33. console.log(kissy.features); 的时候。同上,kissy实例上没有,那么去原型上找。
  34. 刚好原型上有,就直接返回,但是注意,这个原型对象中features属性值已经变化了。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值