JS基础——JavaScript原型和原型链及实际应用

构造函数

function Stu(name,age){
this.name=name;
this.age=age;
}

instanceof 查看引用类型对象是属于哪个构造函数的方法,通过__proto__ 一直往上找prototype,直到找到Object

原型规则和示例

所有的引用类型(数组/对象/函数)都可有扩展属性,都有一个隐式原型__proto__属性,所有函数都可以扩展prototype显示原型属性,属性值是个普通的对象,

所有的引用类型试图得到一个它本身的某个属性,会通过它的隐式原型__proto__去找它构造函数中的prototype

// 不希望打印出原型的属性,只打印自身属性

for(let item of f){
     if(f.hasOwnProperty(item)){
console.log(item)
}
}

  

原型链

let f =new Foo();
f.alertName= function(){
// 方法具体实现
}
f.toString() //toString方法在Foo原型中找,找不到接着要去 f.__proto__.__proto__中查找

New一个对象的过程

  • 创建一个新的对象
  • this指向这个新的对象
  • 执行代码,即对this赋值
  • 返回this

原型链继承

function Elem(id){
  this.elem =document.getElementById(id);
}
Elem.prototype.html =function(val){
  if(val){
    return this.elem.innerHTML;
  }
  this.elem.innerHTML= val;
  return this; //将Elem对象返回
}
Elem.prototype.on =function(type,func){
  this.elem.addEventListener(type,func);
  return this;
}
const elem =new Elem('div1');
elem.html('张三').on('click',function(){
console.log('点击了文本,打印张三');
}).html(‘李四’)

实际应用

新建js文件my-z.js,如下是my-z.js文件源码,解释: slice.call能将具有length属性的对象转成数组 document.querySelectorAll(selector) 是个类数组, 没有.slice 原型,所以需要call,

… 或者[].slice.call(arguments, 0)
(function(window){

    var Z = function(dom,selector) {
        var i,len =dom? dom.length: 0;
        for(i=0;i<dom.length;i++){
            this[i] =dom[i];
        }
        this.length =len;
        this.selector =selector || ''
    }

    var zepto = {}
    zepto.Z= function(dom,selector){
        return new Z(dom,selector)
    }
    zepto.init =function(selector){
        var slice = Array.prototype.slice;
        var dom =slice.call(document.querySelectorAll(selector)) //slice.call能将具有length属性的对象转成数组 document.querySelectorAll(selector) 是个类数组, 没有.slice 原型,所以需要call
        return zepto.Z(dom,selector);
    }
    var $=function(selector){
       return zepto.init(selector)
    }
    $.fn = {
        css : function(key,value) {
            alert(key+':'+value);
            return this;
        },
        html : function() {
            return this[0].innerHTML;
        }
    }
    Z.prototype = $.fn;
    window.$=$;
})(window);

  在index.html 引用该文件

<!DOCTYPE html>
<html >
<head>
    <meta charset="UTF-8">
    <title>prototype</title>
<body>
<div >div</div>
<p>title1</p>
<p>title2</p>
<p>title3</p>
<p>title4</p>
<!--<script type="application/javascript" src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>-->
<script type="application/javascript" src="my-z.js"></script>
</head>
<script type="application/javascript">
    $('#div1').css('font-size','25px').css('color','#1ecc24') //链式调用
    alert($('#div1').html())
</script>
</body>
</html>

JS插件扩展

为什么要把原型方法放在$.fn?

  1. 只有$会暴露在window 全局变量,暴露多个变量容易造成变量的污染
  2. 将插件扩展到$.fn.xxx这个web api 中,方便使用

示例如下所示:

$.fn.extend = function() {
    var options, name, src, copy, copyIsArray, clone,
        target = arguments[0] || {},
        i = 1,
        length = arguments.length,
        deep = false;
    if ( typeof target === "boolean" ) {
        deep = target;
        target = arguments[1] || {};
        i = 2;
    }
    if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
        target = {};
    }
    if ( length === i ) {
        target = this;
        --i;
    }
    for ( ; i < length; i++ ) {
        if ( (options = arguments[ i ]) != null ) {
            for ( name in options ) {
                src = target[ name ];
                copy = options[ name ];
                if ( target === copy ) {
                    continue;
                }
                // 当用户想要深度操作时,递归合并
                // copy是纯对象或者是数组
                if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
                    // 如果是数组
                    if ( copyIsArray ) {
                        // 将copyIsArray重新设置为false,为下次遍历做准备。
                        copyIsArray = false;
                        // 判断被扩展的对象中src是不是数组
                        clone = src && jQuery.isArray(src) ? src : [];
                    } else {
                        // 判断被扩展的对象中src是不是纯对象
                        clone = src && jQuery.isPlainObject(src) ? src : {};
                    }

                    // 递归调用extend方法,继续进行深度遍历
                    target[ name ] = jQuery.extend( deep, clone, copy );

                    // 如果不需要深度复制,则直接把copy(第i个被扩展对象中被遍历的那个键的值)
                } else if ( copy !== undefined ) {
                    target[ name ] = copy;
                }
            }
        }
    }
    // 原对象被改变,因此如果不想改变原对象,target可传入{}
    return target;
};
$.fn.highlight = function (options) {
    // 合并默认值和用户设定值:
    var opts = $.fn.extend({}, $.fn.highlight.defaults, options);
    this.css('backgroundColor', opts.backgroundColor).css('color', opts.color);
    return this;
}

// 设定默认值:
$.fn.highlight.defaults = {
    color: '#d8d030',
    backgroundColor: '#ecfdde'
}

验证插件效果:

<script type="application/javascript">
    //验证插件扩展
    $('#div1').highlight({
        color: '#fff',
        backgroundColor: '#333'
    })
</script>