1. 数据类型判断
  2. 原型,实例,对象,构造函数,创建对象,继承
  3. 实现new,instanceof,call, apply, bind

JS中数据类型的判断

  1. typeof 对于原始类型来说,除了 null 都可以显示正确的类型
  1. console.log(typeof 2); // number
  2. console.log(typeof true); // boolean
  3. console.log(typeof 'str'); // string
  4. console.log(typeof []); // object []数组的数据类型在 typeof 中被解释为 object
  5. console.log(typeof function(){}); // function
  6. console.log(typeof {}); // object
  7. console.log(typeof undefined); // undefined
  8. console.log(typeof null); // object null 的数据类型被 typeof 解释为 object
  • typeof 对于对象来说,除了函数都会显示 object,像数组和对象都是object,所以想判断一个对象的正确类型,这时候需要使用 instanceof
  1. instanceof
    instanceof 可以正确的判断对象的类型,因为内部机制是通过判断对象的原型链中是不是能找到类型的 prototype。
  1. console.log(2 instanceof Number); // false
  2. console.log(true instanceof Boolean); // false
  3. console.log('str' instanceof String); // false
  4. console.log([] instanceof Array); // true
  5. console.log(function(){} instanceof Function); // true
  6. console.log({} instanceof Object); // true
  7. // console.log(undefined instanceof Undefined);
  8. // console.log(null instanceof Null);
  • instanceof可以精准判断引用数据类型(Array,Function,Object),而基本数据类型不能被instanceof精准判断。
    instanceof 在MDN中的解释:instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。
  1. constructor
  1. console.log((2).constructor === Number); // true
  2. console.log((true).constructor === Boolean); // true
  3. console.log(('str').constructor === String); // true
  4. console.log(([]).constructor === Array); // true
  5. console.log((function() {}).constructor === Function); // true
  6. console.log(({}).constructor === Object); // true
  7. 这里有一个坑,如果我创建一个对象,更改它的原型,constructor就会变得不可靠了
  8. function Fn(){};
  9. Fn.prototype=new Array();
  10. var f=new Fn();
  11. console.log(f.constructor===Fn); // false
  12. console.log(f.constructor===Array); // true
  1. Object.prototype.toString.call() 使用 Object 对象的原型方法 toString ,使用 call 进行狸猫换太子,借用Object的 toString 方法
  1. var a = Object.prototype.toString;
  2. console.log(a.call(2));
  3. console.log(a.call(true));
  4. console.log(a.call('str'));
  5. console.log(a.call([]));
  6. console.log(a.call(function(){}));
  7. console.log(a.call({}));
  8. console.log(a.call(undefined));
  9. console.log(a.call(null));

原型,实例,对象,构造函数

js-base-interview1 - 图1

创建对象的方式

  1. function Person(name) {
  2. this.name = name;
  3. }
  4. Person.prototype = {
  5. constructor: Person,
  6. getName: function () {
  7. console.log(this.name);
  8. }
  9. }
  10. var person = new Person("james");

继承的方式

  1. function Parent(name) {
  2. this.name = name;
  3. this.colors = ['red', 'blue', 'green'];
  4. }
  5. Parent.prototype.getName = function () {
  6. console.log(this.name)
  7. }
  8. function Child(name, age) {
  9. Parent.call(this, name);
  10. this.age = age;
  11. }
  12. Child.prototype = new Parent();
  13. Child.prototype.constructor = Child;
  14. var child1 = new Child('devin', '18');
  15. child1.colors.push('black');
  16. console.log(child1.name); // devin
  17. console.log(child1.age); // 18
  18. console.log(child1.colors); // ["red", "blue", "green", "black"]
  19. var child2 = new Child('young', '20');
  20. console.log(child2.name); // daisy
  21. console.log(child2.age); // 20
  22. console.log(child2.colors); // ["red", "blue", "green"]

手写实现new

  1. 创建一个简单的空对象
  2. 链接该对象(即设置该对象的构造函数)到另一个对象 ;
  3. 将创建的新的对象作为this的上下文
  4. 如果该函数没有返回对象则返回this, 如果返回值是一个对象就返回该对象,否则返回构造函数的一个实例对象
  1. function create(){
  2. var obj = new Object();
  3. let con = [].shift.call(arguments);
  4. obj.__proto__ = con.prototype;
  5. con.apply(obj, arguments);
  6. return obj;
  7. }
  8. function Car(color){
  9. this.color = color;
  10. }
  11. Car.prototype.start = function() {
  12. console.log(this.color + "zhaobuchu");
  13. }
  14. var car = create(Car, "blue")
  15. console.log(car.color);
  16. console.log(car.start());
  1. function create() {
  2. Con = [].shift.call(arguments);
  3. var obj = Object.create(Con.prototype);
  4. var ret = Con.apply(obj,arguments);
  5. return ret instanceof Object ? ret : obj;
  6. }
  7. function Car(color, name) {
  8. this.color = color;
  9. return {
  10. name: name
  11. }
  12. }
  13. var car = create(Car, "blue", "宝马")
  14. console.log(car.color);
  15. console.log(car.name)

手写实现instanceOf

instanceof 可以正确的判断对象的类型,因为内部机制是通过判断对象的原型链中是不是能找到类型的 prototype。

实现 instanceof:

  1. 首先获取类型的原型
  2. 然后获得对象的原型
  3. 然后一直循环判断对象的原型是否等于类型的原型,直到对象原型为 null,因为原型链最终为 null
  1. function instanceOf(left, right){
  2. let proto = left._proto_;
  3. let prototype = right.prototype;
  4. while (true) {
  5. if(proto === null)
  6. return false;
  7. if(proto === prototype)
  8. return true;
  9. proto = proto._proto_;
  10. }
  11. }

手写实现call

  1. 用this获取这个函数并设置为对象的属性
  2. 执行这个函数
  3. 删除这个函数
  4. 指定this到函数并传入给定参数执行函数,如果不传入参数,默认指向为 window
  1. Function.prototype.call2 = function(context = window) {
  2. context.fn = this;
  3. let args = [...arguments].slice(1);
  4. context.fn(...args);
  5. delete context.fn;
  6. }
  7. var foo = {
  8. value: 'zhaobuchu',
  9. }
  10. function bar(name, age) {
  11. console.log(name);
  12. console.log(age);
  13. console.log(this.value);
  14. }
  15. bar.call2(foo, 'yangwenxu', 22)

手写实现apply

  1. Function.prototype.apply2 = function(context = window) {
  2. context.fn = this;
  3. if(arguments[1]) {
  4. var result = context.fn(...arguments[1])
  5. } else {
  6. var result = context.fn();
  7. }
  8. delete context.fn;
  9. return result;
  10. }
  11. var obj = {
  12. value: "zhaobuchu"
  13. }
  14. function bar(name, age) {
  15. console.log(name);
  16. console.log(age);
  17. console.log(this.value);
  18. }
  19. bar.apply2(obj, ["yangwenxu", 22]);

手写实现bind

  1. 使用 call / apply 指定 this 。
  2. 使用 return 返回一个函数。
  3. 使用 arguments 获取参数数组并作为 self.apply() 的第二个参数。
  4. 获取返回函数的参数,然后同第3点的参数合并成一个参数数组,并作为 self.apply() 的第二个参数.
  1. Function.prototype.bind2 = function(context){
  2. if (typeof this !== "function") {
  3. throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
  4. }
  5. let self = this;
  6. var args = Array.prototype.slice.call(arguments, 1)
  7. return function() {
  8. var bindArgs = [].slice.call(arguments);
  9. return self.apply(context, args.concat(bindArgs));
  10. }
  11. }
  12. var value = 2;
  13. var foo = {
  14. value: "zhaobuchu",
  15. }
  16. function bar(name, age){
  17. return {
  18. value: this.value,
  19. name: name,
  20. age: age,
  21. }
  22. }
  23. var bindFoo = bar.bind2(foo, "james");
  24. bindFoo(22);

一个绑定函数也能使用new操作符创建对象:
这种行为就像把原函数当成构造器,提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。

  1. Function.prototype.bind2 = function (context) {
  2. var self = this;
  3. var args = Array.prototype.slice.call(arguments, 1);
  4. var fBound = function () {
  5. var bindArgs = Array.prototype.slice.call(arguments);
  6. // 注释1
  7. return self.apply(
  8. this instanceof fBound ? this : context,
  9. args.concat(bindArgs)
  10. );
  11. }
  12. // 注释2
  13. fBound.prototype = this.prototype;
  14. return fBound;
  15. }
  16. var value = 2;
  17. var foo = {
  18. value: "zhaobuchu",
  19. }
  20. function bar(name, age) {
  21. this.habit = 'shopping';
  22. console.log(this.value);
  23. console.log(name);
  24. console.log(age);
  25. }
  26. bar.prototype.friend = 'kevin';
  27. var bindFoo = bar.bind2(foo, 'Jack');
  28. var obj = new bindFoo(20);
  29. console.log(obj.habit);
  30. console.log(obj.friend);

bind实现需要考虑实例化后对原型链的影响。
用一个空对象作为中介,把 fBound.prototype 赋值为空对象的实例(原型式继承)。

  1. Function.prototype.bind2 = function (context) {
  2. if (typeof this !== "function") {
  3. throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
  4. }
  5. var self = this;
  6. var args = Array.prototype.slice.call(arguments, 1);
  7. var fNOP = function () {};
  8. var fBound = function () {
  9. var bindArgs = Array.prototype.slice.call(arguments);
  10. return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
  11. }
  12. fNOP.prototype = this.prototype;
  13. fBound.prototype = new fNOP();
  14. return fBound;
  15. }